bluenevus commited on
Commit
7eacc0a
·
1 Parent(s): 8aa81e7

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +64 -45
app.py CHANGED
@@ -219,9 +219,6 @@ def get_left_col_content():
219
  )
220
  return [
221
  html.H4("Proposal Writer", className="mt-3 mb-2", style={'marginBottom': '12px'}),
222
- html.Div([
223
- html.Div(className="blinking-dot", style={'margin':'0 auto','width':'16px','height':'16px'}),
224
- ], style={'textAlign':'center', 'marginBottom':'10px'}),
225
  html.Hr(style={'marginTop': '8px', 'marginBottom': '16px'}),
226
  html.Div(
227
  id='doc-type-buttons'
@@ -231,9 +228,6 @@ def get_left_col_content():
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'}),
236
- ], style={'textAlign':'center', 'marginBottom':'10px'}))
237
  controls.append(dcc.Loading(
238
  id="loading-indicator",
239
  type="dot",
@@ -351,6 +345,7 @@ app.layout = dbc.Container([
351
  dcc.Store(id='store-loe'),
352
  dcc.Store(id='store-virtual-board'),
353
  dcc.Store(id='store-generated-doc'),
 
354
  dbc.Row([
355
  dbc.Col(
356
  html.H2(id='main-title', className="mt-3 mb-2", style={'textAlign': 'center', 'width':'100%'}),
@@ -359,9 +354,13 @@ app.layout = dbc.Container([
359
  ]),
360
  dbc.Row([
361
  dbc.Col(
362
- html.Div([
363
- html.Div(className="blinking-dot", style={'margin':'0 auto','width':'16px','height':'16px'}),
364
- ], style={'textAlign':'center', 'marginBottom':'10px'})
 
 
 
 
365
  )
366
  ]),
367
  dbc.Row([
@@ -456,7 +455,8 @@ def update_right_col(selected_type, shred, pink, pink_review, red, red_review, g
456
  [
457
  Output('store-shred', 'data'),
458
  Output('file-list', 'children'),
459
- Output('uploaded-doc-name-shred', 'children')
 
460
  ],
461
  [
462
  Input({'type': 'upload-doc-type', 'subtype': 'shred', 'index': 'Shred'}, 'contents')
@@ -493,18 +493,19 @@ def handle_shred_upload(contents, filenames, current_shred, file_list):
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
  @app.callback(
499
  Output('store-pink', 'data'),
500
  Output('uploaded-doc-name-pink', 'children'),
 
501
  Input({'type': 'upload-doc-type', 'subtype': 'pink', 'index': ALL}, 'contents'),
502
  State({'type': 'upload-doc-type', 'subtype': 'pink', 'index': ALL}, 'filename'),
503
  prevent_initial_call=True
504
  )
505
  def handle_pink_upload(contents_list, filenames_list):
506
  if not contents_list or not filenames_list:
507
- return dash.no_update, dash.no_update
508
  for contents, filenames in zip(contents_list, filenames_list):
509
  if contents and filenames:
510
  if isinstance(contents, str):
@@ -512,19 +513,20 @@ def handle_pink_upload(contents_list, filenames_list):
512
  filenames = [filenames]
513
  for content, name in zip(contents, filenames):
514
  file_text = process_document(content, name)
515
- return file_text, name
516
- return dash.no_update, dash.no_update
517
 
518
  @app.callback(
519
  Output('store-pink-review', 'data'),
520
  Output('uploaded-doc-name-pink_review', 'children'),
 
521
  Input({'type': 'upload-doc-type', 'subtype': 'pink_review', 'index': ALL}, 'contents'),
522
  State({'type': 'upload-doc-type', 'subtype': 'pink_review', 'index': ALL}, 'filename'),
523
  prevent_initial_call=True
524
  )
525
  def handle_pink_review_upload(contents_list, filenames_list):
526
  if not contents_list or not filenames_list:
527
- return dash.no_update, dash.no_update
528
  for contents, filenames in zip(contents_list, filenames_list):
529
  if contents and filenames:
530
  if isinstance(contents, str):
@@ -532,19 +534,20 @@ def handle_pink_review_upload(contents_list, filenames_list):
532
  filenames = [filenames]
533
  for content, name in zip(contents, filenames):
534
  file_text = process_document(content, name)
535
- return file_text, name
536
- return dash.no_update, dash.no_update
537
 
538
  @app.callback(
539
  Output('store-red', 'data'),
540
  Output('uploaded-doc-name-red', 'children'),
 
541
  Input({'type': 'upload-doc-type', 'subtype': 'red', 'index': ALL}, 'contents'),
542
  State({'type': 'upload-doc-type', 'subtype': 'red', 'index': ALL}, 'filename'),
543
  prevent_initial_call=True
544
  )
545
  def handle_red_upload(contents_list, filenames_list):
546
  if not contents_list or not filenames_list:
547
- return dash.no_update, dash.no_update
548
  for contents, filenames in zip(contents_list, filenames_list):
549
  if contents and filenames:
550
  if isinstance(contents, str):
@@ -552,19 +555,20 @@ def handle_red_upload(contents_list, filenames_list):
552
  filenames = [filenames]
553
  for content, name in zip(contents, filenames):
554
  file_text = process_document(content, name)
555
- return file_text, name
556
- return dash.no_update, dash.no_update
557
 
558
  @app.callback(
559
  Output('store-red-review', 'data'),
560
  Output('uploaded-doc-name-red_review', 'children'),
 
561
  Input({'type': 'upload-doc-type', 'subtype': 'red_review', 'index': ALL}, 'contents'),
562
  State({'type': 'upload-doc-type', 'subtype': 'red_review', 'index': ALL}, 'filename'),
563
  prevent_initial_call=True
564
  )
565
  def handle_red_review_upload(contents_list, filenames_list):
566
  if not contents_list or not filenames_list:
567
- return dash.no_update, dash.no_update
568
  for contents, filenames in zip(contents_list, filenames_list):
569
  if contents and filenames:
570
  if isinstance(contents, str):
@@ -572,19 +576,20 @@ def handle_red_review_upload(contents_list, filenames_list):
572
  filenames = [filenames]
573
  for content, name in zip(contents, filenames):
574
  file_text = process_document(content, name)
575
- return file_text, name
576
- return dash.no_update, dash.no_update
577
 
578
  @app.callback(
579
  Output('store-gold', 'data'),
580
  Output('uploaded-doc-name-gold', 'children'),
 
581
  Input({'type': 'upload-doc-type', 'subtype': 'gold', 'index': ALL}, 'contents'),
582
  State({'type': 'upload-doc-type', 'subtype': 'gold', 'index': ALL}, 'filename'),
583
  prevent_initial_call=True
584
  )
585
  def handle_gold_upload(contents_list, filenames_list):
586
  if not contents_list or not filenames_list:
587
- return dash.no_update, dash.no_update
588
  for contents, filenames in zip(contents_list, filenames_list):
589
  if contents and filenames:
590
  if isinstance(contents, str):
@@ -592,19 +597,20 @@ def handle_gold_upload(contents_list, filenames_list):
592
  filenames = [filenames]
593
  for content, name in zip(contents, filenames):
594
  file_text = process_document(content, name)
595
- return file_text, name
596
- return dash.no_update, dash.no_update
597
 
598
  @app.callback(
599
  Output('store-gold-review', 'data'),
600
  Output('uploaded-doc-name-gold_review', 'children'),
 
601
  Input({'type': 'upload-doc-type', 'subtype': 'gold_review', 'index': ALL}, 'contents'),
602
  State({'type': 'upload-doc-type', 'subtype': 'gold_review', 'index': ALL}, 'filename'),
603
  prevent_initial_call=True
604
  )
605
  def handle_gold_review_upload(contents_list, filenames_list):
606
  if not contents_list or not filenames_list:
607
- return dash.no_update, dash.no_update
608
  for contents, filenames in zip(contents_list, filenames_list):
609
  if contents and filenames:
610
  if isinstance(contents, str):
@@ -612,19 +618,20 @@ def handle_gold_review_upload(contents_list, filenames_list):
612
  filenames = [filenames]
613
  for content, name in zip(contents, filenames):
614
  file_text = process_document(content, name)
615
- return file_text, name
616
- return dash.no_update, dash.no_update
617
 
618
  @app.callback(
619
  Output('store-loe', 'data'),
620
  Output('uploaded-doc-name-loe', 'children'),
 
621
  Input({'type': 'upload-doc-type', 'subtype': 'loe', 'index': ALL}, 'contents'),
622
  State({'type': 'upload-doc-type', 'subtype': 'loe', 'index': ALL}, 'filename'),
623
  prevent_initial_call=True
624
  )
625
  def handle_loe_upload(contents_list, filenames_list):
626
  if not contents_list or not filenames_list:
627
- return dash.no_update, dash.no_update
628
  for contents, filenames in zip(contents_list, filenames_list):
629
  if contents and filenames:
630
  if isinstance(contents, str):
@@ -632,19 +639,20 @@ def handle_loe_upload(contents_list, filenames_list):
632
  filenames = [filenames]
633
  for content, name in zip(contents, filenames):
634
  file_text = process_document(content, name)
635
- return file_text, name
636
- return dash.no_update, dash.no_update
637
 
638
  @app.callback(
639
  Output('store-virtual-board', 'data'),
640
  Output('uploaded-doc-name-virtual_board', 'children'),
 
641
  Input({'type': 'upload-doc-type', 'subtype': 'virtual_board', 'index': ALL}, 'contents'),
642
  State({'type': 'upload-doc-type', 'subtype': 'virtual_board', 'index': ALL}, 'filename'),
643
  prevent_initial_call=True
644
  )
645
  def handle_virtual_board_upload(contents_list, filenames_list):
646
  if not contents_list or not filenames_list:
647
- return dash.no_update, dash.no_update
648
  for contents, filenames in zip(contents_list, filenames_list):
649
  if contents and filenames:
650
  if isinstance(contents, str):
@@ -652,11 +660,12 @@ def handle_virtual_board_upload(contents_list, filenames_list):
652
  filenames = [filenames]
653
  for content, name in zip(contents, filenames):
654
  file_text = process_document(content, name)
655
- return file_text, name
656
- return dash.no_update, dash.no_update
657
 
658
  @app.callback(
659
  Output('document-preview', 'children'),
 
660
  [
661
  Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
662
  Input('selected-doc-type', 'data'),
@@ -718,10 +727,10 @@ def preview_or_generate_doc(n_clicks_list, selected_type, shred, pink, pink_revi
718
  else:
719
  preview = markdown_narrative_preview(generated_doc)
720
  logging.info("Document preview updated.")
721
- return preview
722
  else:
723
  if selected_type == "Shred" and shred:
724
- return markdown_table_preview(shred)
725
  elif selected_type in spreadsheet_types:
726
  doc_store = {
727
  "Pink Review": pink_review,
@@ -731,7 +740,7 @@ def preview_or_generate_doc(n_clicks_list, selected_type, shred, pink, pink_revi
731
  "LOE": loe
732
  }
733
  doc = doc_store.get(selected_type, "")
734
- return markdown_table_preview(doc)
735
  elif selected_type in narrative_types:
736
  doc_store = {
737
  "Pink": pink,
@@ -739,11 +748,12 @@ def preview_or_generate_doc(n_clicks_list, selected_type, shred, pink, pink_revi
739
  "Gold": gold
740
  }
741
  doc = doc_store.get(selected_type, "")
742
- return markdown_narrative_preview(doc)
743
- return html.Div("No document loaded.")
744
 
745
  @app.callback(
746
  Output('store-generated-doc', 'data'),
 
747
  [
748
  Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
749
  Input('selected-doc-type', 'data'),
@@ -767,11 +777,12 @@ def update_generated_doc(n_clicks_list, selected_type, preview_content, shred, p
767
  ctx = callback_context
768
  trigger = ctx.triggered[0]['prop_id'] if ctx.triggered else ""
769
  if any(n_clicks_list):
770
- return app.server.config.get('last_generated_doc', prev_generated)
771
- return prev_generated
772
 
773
  @app.callback(
774
  Output("download-document", "data"),
 
775
  Input("btn-download", "n_clicks"),
776
  State('selected-doc-type', 'data'),
777
  State('store-generated-doc', 'data'),
@@ -809,15 +820,23 @@ def download_document(n_clicks, selected_type, generated_doc, shred, pink, pink_
809
  doc = virtual_board
810
  if not doc:
811
  logging.warning("No generated document to download.")
812
- return dash.no_update
813
  if selected_type in spreadsheet_types:
814
  xlsx_io = markdown_tables_to_xlsx(doc)
815
  logging.info("Spreadsheet document prepared for download.")
816
- return dcc.send_bytes(xlsx_io.getvalue(), filename=f"{selected_type}_output.xlsx")
817
  else:
818
  content = doc.encode('utf-8')
819
  logging.info("Narrative document prepared for download.")
820
- return dcc.send_bytes(content, filename=f"{selected_type}_output.md")
 
 
 
 
 
 
 
 
821
 
822
  if __name__ == '__main__':
823
  print("Starting the Dash application...")
 
219
  )
220
  return [
221
  html.H4("Proposal Writer", className="mt-3 mb-2", style={'marginBottom': '12px'}),
 
 
 
222
  html.Hr(style={'marginTop': '8px', 'marginBottom': '16px'}),
223
  html.Div(
224
  id='doc-type-buttons'
 
228
 
229
  def get_right_col_content(selected_type, store_data):
230
  controls = []
 
 
 
231
  controls.append(dcc.Loading(
232
  id="loading-indicator",
233
  type="dot",
 
345
  dcc.Store(id='store-loe'),
346
  dcc.Store(id='store-virtual-board'),
347
  dcc.Store(id='store-generated-doc'),
348
+ dcc.Store(id='progress-dot-store'),
349
  dbc.Row([
350
  dbc.Col(
351
  html.H2(id='main-title', className="mt-3 mb-2", style={'textAlign': 'center', 'width':'100%'}),
 
354
  ]),
355
  dbc.Row([
356
  dbc.Col(
357
+ dcc.Loading(
358
+ id="main-progress-dot",
359
+ type="dot",
360
+ children=html.Div(id="main-progress-dot-output")
361
+ ),
362
+ width=12,
363
+ style={'textAlign':'center', 'marginBottom':'10px'}
364
  )
365
  ]),
366
  dbc.Row([
 
455
  [
456
  Output('store-shred', 'data'),
457
  Output('file-list', 'children'),
458
+ Output('uploaded-doc-name-shred', 'children'),
459
+ Output('progress-dot-store', 'data')
460
  ],
461
  [
462
  Input({'type': 'upload-doc-type', 'subtype': 'shred', 'index': 'Shred'}, 'contents')
 
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], "progress"
497
 
498
  @app.callback(
499
  Output('store-pink', 'data'),
500
  Output('uploaded-doc-name-pink', 'children'),
501
+ Output('progress-dot-store', 'data'),
502
  Input({'type': 'upload-doc-type', 'subtype': 'pink', 'index': ALL}, 'contents'),
503
  State({'type': 'upload-doc-type', 'subtype': 'pink', 'index': ALL}, 'filename'),
504
  prevent_initial_call=True
505
  )
506
  def handle_pink_upload(contents_list, filenames_list):
507
  if not contents_list or not filenames_list:
508
+ return dash.no_update, dash.no_update, dash.no_update
509
  for contents, filenames in zip(contents_list, filenames_list):
510
  if contents and filenames:
511
  if isinstance(contents, str):
 
513
  filenames = [filenames]
514
  for content, name in zip(contents, filenames):
515
  file_text = process_document(content, name)
516
+ return file_text, name, "progress"
517
+ return dash.no_update, dash.no_update, dash.no_update
518
 
519
  @app.callback(
520
  Output('store-pink-review', 'data'),
521
  Output('uploaded-doc-name-pink_review', 'children'),
522
+ Output('progress-dot-store', 'data'),
523
  Input({'type': 'upload-doc-type', 'subtype': 'pink_review', 'index': ALL}, 'contents'),
524
  State({'type': 'upload-doc-type', 'subtype': 'pink_review', 'index': ALL}, 'filename'),
525
  prevent_initial_call=True
526
  )
527
  def handle_pink_review_upload(contents_list, filenames_list):
528
  if not contents_list or not filenames_list:
529
+ return dash.no_update, dash.no_update, dash.no_update
530
  for contents, filenames in zip(contents_list, filenames_list):
531
  if contents and filenames:
532
  if isinstance(contents, str):
 
534
  filenames = [filenames]
535
  for content, name in zip(contents, filenames):
536
  file_text = process_document(content, name)
537
+ return file_text, name, "progress"
538
+ return dash.no_update, dash.no_update, dash.no_update
539
 
540
  @app.callback(
541
  Output('store-red', 'data'),
542
  Output('uploaded-doc-name-red', 'children'),
543
+ Output('progress-dot-store', 'data'),
544
  Input({'type': 'upload-doc-type', 'subtype': 'red', 'index': ALL}, 'contents'),
545
  State({'type': 'upload-doc-type', 'subtype': 'red', 'index': ALL}, 'filename'),
546
  prevent_initial_call=True
547
  )
548
  def handle_red_upload(contents_list, filenames_list):
549
  if not contents_list or not filenames_list:
550
+ return dash.no_update, dash.no_update, dash.no_update
551
  for contents, filenames in zip(contents_list, filenames_list):
552
  if contents and filenames:
553
  if isinstance(contents, str):
 
555
  filenames = [filenames]
556
  for content, name in zip(contents, filenames):
557
  file_text = process_document(content, name)
558
+ return file_text, name, "progress"
559
+ return dash.no_update, dash.no_update, dash.no_update
560
 
561
  @app.callback(
562
  Output('store-red-review', 'data'),
563
  Output('uploaded-doc-name-red_review', 'children'),
564
+ Output('progress-dot-store', 'data'),
565
  Input({'type': 'upload-doc-type', 'subtype': 'red_review', 'index': ALL}, 'contents'),
566
  State({'type': 'upload-doc-type', 'subtype': 'red_review', 'index': ALL}, 'filename'),
567
  prevent_initial_call=True
568
  )
569
  def handle_red_review_upload(contents_list, filenames_list):
570
  if not contents_list or not filenames_list:
571
+ return dash.no_update, dash.no_update, dash.no_update
572
  for contents, filenames in zip(contents_list, filenames_list):
573
  if contents and filenames:
574
  if isinstance(contents, str):
 
576
  filenames = [filenames]
577
  for content, name in zip(contents, filenames):
578
  file_text = process_document(content, name)
579
+ return file_text, name, "progress"
580
+ return dash.no_update, dash.no_update, dash.no_update
581
 
582
  @app.callback(
583
  Output('store-gold', 'data'),
584
  Output('uploaded-doc-name-gold', 'children'),
585
+ Output('progress-dot-store', 'data'),
586
  Input({'type': 'upload-doc-type', 'subtype': 'gold', 'index': ALL}, 'contents'),
587
  State({'type': 'upload-doc-type', 'subtype': 'gold', 'index': ALL}, 'filename'),
588
  prevent_initial_call=True
589
  )
590
  def handle_gold_upload(contents_list, filenames_list):
591
  if not contents_list or not filenames_list:
592
+ return dash.no_update, dash.no_update, dash.no_update
593
  for contents, filenames in zip(contents_list, filenames_list):
594
  if contents and filenames:
595
  if isinstance(contents, str):
 
597
  filenames = [filenames]
598
  for content, name in zip(contents, filenames):
599
  file_text = process_document(content, name)
600
+ return file_text, name, "progress"
601
+ return dash.no_update, dash.no_update, dash.no_update
602
 
603
  @app.callback(
604
  Output('store-gold-review', 'data'),
605
  Output('uploaded-doc-name-gold_review', 'children'),
606
+ Output('progress-dot-store', 'data'),
607
  Input({'type': 'upload-doc-type', 'subtype': 'gold_review', 'index': ALL}, 'contents'),
608
  State({'type': 'upload-doc-type', 'subtype': 'gold_review', 'index': ALL}, 'filename'),
609
  prevent_initial_call=True
610
  )
611
  def handle_gold_review_upload(contents_list, filenames_list):
612
  if not contents_list or not filenames_list:
613
+ return dash.no_update, dash.no_update, dash.no_update
614
  for contents, filenames in zip(contents_list, filenames_list):
615
  if contents and filenames:
616
  if isinstance(contents, str):
 
618
  filenames = [filenames]
619
  for content, name in zip(contents, filenames):
620
  file_text = process_document(content, name)
621
+ return file_text, name, "progress"
622
+ return dash.no_update, dash.no_update, dash.no_update
623
 
624
  @app.callback(
625
  Output('store-loe', 'data'),
626
  Output('uploaded-doc-name-loe', 'children'),
627
+ Output('progress-dot-store', 'data'),
628
  Input({'type': 'upload-doc-type', 'subtype': 'loe', 'index': ALL}, 'contents'),
629
  State({'type': 'upload-doc-type', 'subtype': 'loe', 'index': ALL}, 'filename'),
630
  prevent_initial_call=True
631
  )
632
  def handle_loe_upload(contents_list, filenames_list):
633
  if not contents_list or not filenames_list:
634
+ return dash.no_update, dash.no_update, dash.no_update
635
  for contents, filenames in zip(contents_list, filenames_list):
636
  if contents and filenames:
637
  if isinstance(contents, str):
 
639
  filenames = [filenames]
640
  for content, name in zip(contents, filenames):
641
  file_text = process_document(content, name)
642
+ return file_text, name, "progress"
643
+ return dash.no_update, dash.no_update, dash.no_update
644
 
645
  @app.callback(
646
  Output('store-virtual-board', 'data'),
647
  Output('uploaded-doc-name-virtual_board', 'children'),
648
+ Output('progress-dot-store', 'data'),
649
  Input({'type': 'upload-doc-type', 'subtype': 'virtual_board', 'index': ALL}, 'contents'),
650
  State({'type': 'upload-doc-type', 'subtype': 'virtual_board', 'index': ALL}, 'filename'),
651
  prevent_initial_call=True
652
  )
653
  def handle_virtual_board_upload(contents_list, filenames_list):
654
  if not contents_list or not filenames_list:
655
+ return dash.no_update, dash.no_update, dash.no_update
656
  for contents, filenames in zip(contents_list, filenames_list):
657
  if contents and filenames:
658
  if isinstance(contents, str):
 
660
  filenames = [filenames]
661
  for content, name in zip(contents, filenames):
662
  file_text = process_document(content, name)
663
+ return file_text, name, "progress"
664
+ return dash.no_update, dash.no_update, dash.no_update
665
 
666
  @app.callback(
667
  Output('document-preview', 'children'),
668
+ Output('progress-dot-store', 'data'),
669
  [
670
  Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
671
  Input('selected-doc-type', 'data'),
 
727
  else:
728
  preview = markdown_narrative_preview(generated_doc)
729
  logging.info("Document preview updated.")
730
+ return preview, "progress"
731
  else:
732
  if selected_type == "Shred" and shred:
733
+ return markdown_table_preview(shred), dash.no_update
734
  elif selected_type in spreadsheet_types:
735
  doc_store = {
736
  "Pink Review": pink_review,
 
740
  "LOE": loe
741
  }
742
  doc = doc_store.get(selected_type, "")
743
+ return markdown_table_preview(doc), dash.no_update
744
  elif selected_type in narrative_types:
745
  doc_store = {
746
  "Pink": pink,
 
748
  "Gold": gold
749
  }
750
  doc = doc_store.get(selected_type, "")
751
+ return markdown_narrative_preview(doc), dash.no_update
752
+ return html.Div("No document loaded."), dash.no_update
753
 
754
  @app.callback(
755
  Output('store-generated-doc', 'data'),
756
+ Output('progress-dot-store', 'data'),
757
  [
758
  Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
759
  Input('selected-doc-type', 'data'),
 
777
  ctx = callback_context
778
  trigger = ctx.triggered[0]['prop_id'] if ctx.triggered else ""
779
  if any(n_clicks_list):
780
+ return app.server.config.get('last_generated_doc', prev_generated), "progress"
781
+ return prev_generated, dash.no_update
782
 
783
  @app.callback(
784
  Output("download-document", "data"),
785
+ Output('progress-dot-store', 'data'),
786
  Input("btn-download", "n_clicks"),
787
  State('selected-doc-type', 'data'),
788
  State('store-generated-doc', 'data'),
 
820
  doc = virtual_board
821
  if not doc:
822
  logging.warning("No generated document to download.")
823
+ return dash.no_update, dash.no_update
824
  if selected_type in spreadsheet_types:
825
  xlsx_io = markdown_tables_to_xlsx(doc)
826
  logging.info("Spreadsheet document prepared for download.")
827
+ return dcc.send_bytes(xlsx_io.getvalue(), filename=f"{selected_type}_output.xlsx"), "progress"
828
  else:
829
  content = doc.encode('utf-8')
830
  logging.info("Narrative document prepared for download.")
831
+ return dcc.send_bytes(content, filename=f"{selected_type}_output.md"), "progress"
832
+
833
+ @app.callback(
834
+ Output("main-progress-dot-output", "children"),
835
+ Input('progress-dot-store', 'data')
836
+ )
837
+ def update_main_progress_dot(progress):
838
+ # Dummy output to trigger dcc.Loading
839
+ return ""
840
 
841
  if __name__ == '__main__':
842
  print("Starting the Dash application...")