bluenevus commited on
Commit
469746c
·
1 Parent(s): 3e8897c

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +370 -367
app.py CHANGED
@@ -452,92 +452,141 @@ def update_right_col(selected_type, shred_doc, pink_doc, pink_review_doc, red_do
452
  @app.callback(
453
  Output('file-list', 'children'),
454
  Output('store-shred', 'data'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  Input('upload-document', 'contents'),
456
  State('upload-document', 'filename'),
457
  State('file-list', 'children'),
458
- State('store-shred', 'data')
459
- )
460
- def update_output(list_of_contents, list_of_names, existing_files, current_shred):
461
- global uploaded_files
462
- if list_of_contents is not None:
463
- new_files = []
464
- for i, (content, name) in enumerate(zip(list_of_contents, list_of_names)):
465
- file_content = process_document(content, name)
466
- uploaded_files[name] = file_content
467
- new_files.append(
468
- dbc.Row([
469
- dbc.Col(
470
- html.Button('×', id={'type': 'remove-file', 'index': name}, style={'marginRight': '5px', 'fontSize': '10px'}),
471
- width=1
472
- ),
473
- dbc.Col(
474
- html.Span(name, style={'wordBreak': 'break-all'}),
475
- width=11
476
- )
477
- ], id={'type': 'file-row', 'index': name}, align="center", className="mb-1")
478
- )
479
- if existing_files is None:
480
- existing_files = []
481
- elif not isinstance(existing_files, list):
482
- existing_files = [existing_files]
483
- logging.info("Documents uploaded and file list updated.")
484
- return existing_files + new_files, current_shred
485
- return existing_files, current_shred
486
-
487
- @app.callback(
488
- Output('file-list', 'children'),
489
- Output('store-shred', 'data'),
490
  Input({'type': 'remove-file', 'index': ALL}, 'n_clicks'),
491
  State('file-list', 'children'),
492
  State('store-shred', 'data'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  prevent_initial_call=True
494
  )
495
- def remove_file(n_clicks, existing_files, current_shred):
496
- global uploaded_files
497
- ctx = dash.callback_context
498
- if not ctx.triggered:
499
- raise dash.exceptions.PreventUpdate
500
- triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
501
- if not triggered_id.startswith("{\"type\":\"remove-file\""):
502
- raise dash.exceptions.PreventUpdate
503
- try:
504
- import json
505
- triggered_dict = json.loads(triggered_id)
506
- removed_file = triggered_dict['index']
507
- except Exception as e:
508
- logging.error(f"Could not parse removed file from callback context: {e}")
509
- raise dash.exceptions.PreventUpdate
510
- uploaded_files.pop(removed_file, None)
511
- logging.info(f"Removed file: {removed_file}")
512
- filtered_files = []
513
- if existing_files:
514
- for file in existing_files:
515
- try:
516
- file_id = file['props']['id']
517
- if file_id['index'] != removed_file:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  filtered_files.append(file)
519
- except Exception as e:
520
- filtered_files.append(file)
521
- return filtered_files, current_shred
522
-
523
- def make_upload_callback(src):
524
- @app.callback(
525
- Output({'type': f'uploaded-doc-name-{src}', 'index': MATCH}, 'children'),
526
- Output({'type': f'upload-doc-type-{src}', 'index': MATCH}, 'contents'),
527
- Output({'type': f'radio-doc-source-{src}', 'index': MATCH}, 'value'),
528
- Input({'type': f'upload-doc-type-{src}', 'index': MATCH}, 'contents'),
529
- State({'type': f'upload-doc-type-{src}', 'index': MATCH}, 'filename'),
530
- State({'type': f'upload-doc-type-{src}', 'index': MATCH}, 'id')
531
- )
532
- def update_uploaded_doc_name(contents, filename, id_dict):
533
- global uploaded_doc_contents
534
- if contents is not None:
535
- uploaded_doc_contents[(src, id_dict['index'])] = (contents, filename)
536
- logging.info(f"{id_dict['index']} {src} file uploaded: {filename}")
537
- return filename, contents, "uploaded"
538
- return "", None, "loaded"
539
- for src in ["shred", "pink", "pink_review", "red", "red_review", "gold"]:
540
- make_upload_callback(src)
 
 
 
541
 
542
  def gemini_generate(prompt, max_tokens=4096, temperature=0.25):
543
  result_holder = {}
@@ -605,6 +654,8 @@ Now, generate the {document_type}:
605
  Output('store-gold-review', 'data'),
606
  Output('store-loe', 'data'),
607
  Output('store-virtual-board', 'data'),
 
 
608
  Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
609
  State({'type': 'btn-generate-doc', 'index': ALL}, 'id'),
610
  State({'type': 'radio-doc-source-shred', 'index': ALL}, 'value'),
@@ -634,9 +685,25 @@ Now, generate the {document_type}:
634
  State('store-gold-review', 'data'),
635
  State('store-loe', 'data'),
636
  State('store-virtual-board', 'data'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
  prevent_initial_call=True
638
  )
639
- def generate_any_doc(
640
  n_clicks_list, btn_ids,
641
  radio_shred, upload_shred_contents, upload_shred_filenames,
642
  radio_pink, upload_pink_contents, upload_pink_filenames,
@@ -644,322 +711,258 @@ def generate_any_doc(
644
  radio_red, upload_red_contents, upload_red_filenames,
645
  radio_red_review, upload_red_review_contents, upload_red_review_filenames,
646
  radio_gold, upload_gold_contents, upload_gold_filenames,
647
- store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board
 
 
648
  ):
649
  ctx = callback_context
650
- logging.info(f"generate_any_doc triggered: n_clicks_list={n_clicks_list}, btn_ids={btn_ids}")
651
- if not ctx.triggered:
652
- raise dash.exceptions.PreventUpdate
653
- idx = [i for i, x in enumerate(n_clicks_list) if x]
654
- if not idx:
655
- raise dash.exceptions.PreventUpdate
656
- idx = idx[-1]
657
- doc_type = btn_ids[idx]['index']
658
-
659
- shred_doc = store_shred
660
- pink_doc = store_pink
661
- pink_review_doc = store_pink_review
662
- red_doc = store_red
663
- red_review_doc = store_red_review
664
- gold_doc = store_gold
665
- gold_review_doc = store_gold_review
666
- loe_doc = store_loe
667
- virtual_board_doc = store_virtual_board
668
-
669
- def get_doc_from_radio(radio, upload_contents, upload_filenames, loaded_var):
670
- if radio and radio[0] == 'uploaded':
671
- if upload_contents and upload_contents[0] and upload_filenames and upload_filenames[0]:
672
- return process_document(upload_contents[0], upload_filenames[0])
 
 
 
 
 
673
  else:
674
- return None
675
- else:
676
- return loaded_var
677
 
678
- if doc_type == "Shred":
679
- if not uploaded_files:
680
- logging.info("No uploaded files for Shred. Aborting.")
681
- return html.Div("Please upload a document before shredding."), "", None, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
682
- file_contents = list(uploaded_files.values())
683
- try:
684
- generated = generate_document(doc_type, file_contents)
685
- shred_doc = generated
686
- preview = markdown_table_preview(generated)
687
- logging.info("Shred document generated.")
688
- return preview, "Shred generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
689
- except Exception as e:
690
- logging.error(f"Error generating document: {str(e)}")
691
- return html.Div(f"Error generating document: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
692
 
693
- if doc_type == "Pink":
694
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
695
- if not shred:
696
- return html.Div("Please provide a Shred requirements document (either loaded or uploaded) to generate Pink."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
697
- try:
698
- generated = generate_document(doc_type, [shred])
699
- pink_doc = generated
700
- preview = markdown_narrative_preview(generated)
701
- logging.info("Pink document generated.")
702
- return preview, "Pink generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
703
- except Exception as e:
704
- logging.error(f"Error generating Pink: {str(e)}")
705
- return html.Div(f"Error generating Pink: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
706
-
707
- if doc_type == "Pink Review":
708
- pink = get_doc_from_radio(radio_pink, upload_pink_contents, upload_pink_filenames, pink_doc)
709
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
710
- if not pink or not shred:
711
- return html.Div("Please provide both Pink and Shred documents (either loaded or uploaded) to generate Pink Review."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
712
- try:
713
- generated = generate_document(doc_type, [pink, shred])
714
- pink_review_doc = generated
715
- preview = markdown_table_preview(generated)
716
- logging.info("Pink Review document generated.")
717
- return preview, "Pink Review generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
718
- except Exception as e:
719
- logging.error(f"Error generating Pink Review: {str(e)}")
720
- return html.Div(f"Error generating Pink Review: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
721
 
722
- if doc_type == "Red":
723
- pink_review = get_doc_from_radio(radio_pink_review, upload_pink_review_contents, upload_pink_review_filenames, pink_review_doc)
724
- if not pink_review:
725
- return html.Div("Please provide a Pink Review document (either loaded or uploaded) to generate Red."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
726
- try:
727
- generated = generate_document(doc_type, [pink_review])
728
- red_doc = generated
729
- preview = markdown_narrative_preview(generated)
730
- logging.info("Red document generated.")
731
- return preview, "Red generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
732
- except Exception as e:
733
- logging.error(f"Error generating Red: {str(e)}")
734
- return html.Div(f"Error generating Red: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
735
-
736
- if doc_type == "Red Review":
737
- red = get_doc_from_radio(radio_red, upload_red_contents, upload_red_filenames, red_doc)
738
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
739
- if not red or not shred:
740
- return html.Div("Please provide both Red and Shred documents (either loaded or uploaded) to generate Red Review."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
741
- try:
742
- generated = generate_document(doc_type, [red, shred])
743
- red_review_doc = generated
744
- preview = markdown_table_preview(generated)
745
- logging.info("Red Review document generated.")
746
- return preview, "Red Review generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
747
- except Exception as e:
748
- logging.error(f"Error generating Red Review: {str(e)}")
749
- return html.Div(f"Error generating Red Review: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
750
 
751
- if doc_type == "Gold":
752
- red_review = get_doc_from_radio(radio_red_review, upload_red_review_contents, upload_red_review_filenames, red_review_doc)
753
- if not red_review:
754
- return html.Div("Please provide a Red Review document (either loaded or uploaded) to generate Gold."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
755
- try:
756
- generated = generate_document(doc_type, [red_review])
757
- gold_doc = generated
758
- preview = markdown_narrative_preview(generated)
759
- logging.info("Gold document generated.")
760
- return preview, "Gold generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
761
- except Exception as e:
762
- logging.error(f"Error generating Gold: {str(e)}")
763
- return html.Div(f"Error generating Gold: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
764
-
765
- if doc_type == "Gold Review":
766
- gold = get_doc_from_radio(radio_gold, upload_gold_contents, upload_gold_filenames, gold_doc)
767
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
768
- if not gold or not shred:
769
- return html.Div("Please provide both Gold and Shred documents (either loaded or uploaded) to generate Gold Review."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
770
- try:
771
- generated = generate_document(doc_type, [gold, shred])
772
- gold_review_doc = generated
773
- preview = markdown_table_preview(generated)
774
- logging.info("Gold Review document generated.")
775
- return preview, "Gold Review generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
776
- except Exception as e:
777
- logging.error(f"Error generating Gold Review: {str(e)}")
778
- return html.Div(f"Error generating Gold Review: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
779
 
780
- if doc_type == "LOE":
781
- gold = get_doc_from_radio(radio_gold, upload_gold_contents, upload_gold_filenames, gold_doc)
782
- if not gold:
783
- return html.Div("Please provide a Gold document (either loaded or uploaded) to generate LOE."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
784
- try:
785
- generated = generate_document(doc_type, [gold])
786
- loe_doc = generated
787
- preview = markdown_table_preview(generated)
788
- logging.info("LOE document generated.")
789
- return preview, "LOE generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
790
- except Exception as e:
791
- logging.error(f"Error generating LOE: {str(e)}")
792
- return html.Div(f"Error generating LOE: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
793
 
794
- if doc_type == "Virtual Board":
795
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
796
- if not shred:
797
- return html.Div("Please provide a Shred requirements document (either loaded or uploaded) to generate Virtual Board."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
798
- try:
799
- lm_text = ""
800
- lm_match = re.search(r'(Section\s+L[\s\S]+?)(Section\s+M|$)', shred, re.IGNORECASE)
801
- if lm_match:
802
- lm_text = lm_match.group(1)
803
- else:
804
- lm_text = shred
805
- generated = generate_document(doc_type, [lm_text])
806
- virtual_board_doc = generated
807
- preview = markdown_table_preview(generated)
808
- logging.info("Virtual Board document generated.")
809
- return preview, "Virtual Board generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
810
- except Exception as e:
811
- logging.error(f"Error generating Virtual Board: {str(e)}")
812
- return html.Div(f"Error generating Virtual Board: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
813
 
814
- return html.Div("Unsupported document type or missing required sources."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc
 
 
 
 
 
 
 
 
 
 
 
815
 
816
- @app.callback(
817
- Output('chat-output', 'children'),
818
- Output('document-preview', 'children'),
819
- Output('store-shred', 'data'),
820
- Output('store-pink', 'data'),
821
- Output('store-pink-review', 'data'),
822
- Output('store-red', 'data'),
823
- Output('store-red-review', 'data'),
824
- Output('store-gold', 'data'),
825
- Output('store-gold-review', 'data'),
826
- Output('store-loe', 'data'),
827
- Output('store-virtual-board', 'data'),
828
- Input('btn-send-chat', 'n_clicks'),
829
- Input('btn-clear-chat', 'n_clicks'),
830
- State('chat-input', 'value'),
831
- State('selected-doc-type', 'data'),
832
- State('document-preview', 'children'),
833
- State('store-shred', 'data'),
834
- State('store-pink', 'data'),
835
- State('store-pink-review', 'data'),
836
- State('store-red', 'data'),
837
- State('store-red-review', 'data'),
838
- State('store-gold', 'data'),
839
- State('store-gold-review', 'data'),
840
- State('store-loe', 'data'),
841
- State('store-virtual-board', 'data'),
842
- prevent_initial_call=True
843
- )
844
- def update_document_via_chat(btn_send, btn_clear, chat_input, selected_doc_type, doc_preview,
845
- store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board):
846
- ctx = callback_context
847
- if not ctx.triggered:
848
- raise dash.exceptions.PreventUpdate
849
- trigger_id = ctx.triggered[0]['prop_id'].split('.')[0]
850
- if trigger_id == 'btn-clear-chat':
851
- return "", dash.no_update, store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board
852
-
853
- doc_map = {
854
- "Shred": store_shred,
855
- "Pink": store_pink,
856
- "Pink Review": store_pink_review,
857
- "Red": store_red,
858
- "Red Review": store_red_review,
859
- "Gold": store_gold,
860
- "Gold Review": store_gold_review,
861
- "LOE": store_loe,
862
- "Virtual Board": store_virtual_board
863
- }
864
- current_document = doc_map.get(selected_doc_type)
865
-
866
- if not chat_input or current_document is None:
867
- raise dash.exceptions.PreventUpdate
868
 
869
- if selected_doc_type in spreadsheet_types:
870
- prompt = f"""Update the following {selected_doc_type} spreadsheet based on this instruction: {chat_input}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
871
  Current spreadsheet (markdown table format):
872
  {current_document}
873
  Instructions:
874
  1. Provide the updated spreadsheet as a markdown table only.
875
  2. Do not include any narrative, only the markdown table.
876
- Now, provide the updated {selected_doc_type} spreadsheet:
877
  """
878
- else:
879
- prompt = f"""Update the following {selected_doc_type} document based on this instruction: {chat_input}
880
  Current document:
881
  {current_document}
882
  Instructions:
883
  1. Provide the updated document content.
884
  2. Maintain proper formatting and structure.
885
  3. Incorporate the requested changes seamlessly.
886
- 4. If the {selected_doc_type} is Pink, Red, or Gold then your goal is to write a FULL proposal response, not just a strategy and be compliant and compelling by addressing all the requirements from the document provided. Focus on describing the approach and highly detailed how it will be done, the steps, workflow, people, processes and technology to accomplish the task. Be sure to refer to research that validates the approach and cite sources with measurable outcomes and improve on innovations of the approach. Do not just say things like we will, or MicroHealth will, use active voice and verbs that are definitive in nature, not maybe, could be, should be, can be and things like that. This is a proposal response so logical flow is important so the reader can follow.
887
- Now, provide the updated {selected_doc_type}:
888
  """
889
- logging.info(f"Updating document via chat for {selected_doc_type} instruction: {chat_input}")
890
- try:
891
- new_document = gemini_generate(prompt, max_tokens=4096, temperature=0.5)
892
- if selected_doc_type in spreadsheet_types:
893
- preview = markdown_table_preview(new_document)
894
- else:
895
- preview = markdown_narrative_preview(new_document)
896
- # Only update the relevant store
897
- stores = [store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board]
898
- doc_types = ["Shred", "Pink", "Pink Review", "Red", "Red Review", "Gold", "Gold Review", "LOE", "Virtual Board"]
899
- new_stores = []
900
- for dt, s in zip(doc_types, stores):
901
- if dt == selected_doc_type:
902
- new_stores.append(new_document)
903
- else:
904
- new_stores.append(s)
905
- return f"Document updated based on: {chat_input}", preview, *new_stores
906
- except Exception as e:
907
- logging.error(f"Error updating document via chat: {str(e)}")
908
- return f"Error updating document: {str(e)}", html.Div(f"Error updating document: {str(e)}"), store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board
909
-
910
- @app.callback(
911
- Output("download-document", "data"),
912
- Input("btn-download", "n_clicks"),
913
- State('selected-doc-type', 'data'),
914
- State('store-shred', 'data'),
915
- State('store-pink', 'data'),
916
- State('store-pink-review', 'data'),
917
- State('store-red', 'data'),
918
- State('store-red-review', 'data'),
919
- State('store-gold', 'data'),
920
- State('store-gold-review', 'data'),
921
- State('store-loe', 'data'),
922
- State('store-virtual-board', 'data'),
923
- prevent_initial_call=True
924
- )
925
- def download_document(n_clicks, selected_doc_type, store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board):
926
- doc_map = {
927
- "Shred": store_shred,
928
- "Pink": store_pink,
929
- "Pink Review": store_pink_review,
930
- "Red": store_red,
931
- "Red Review": store_red_review,
932
- "Gold": store_gold,
933
- "Gold Review": store_gold_review,
934
- "LOE": store_loe,
935
- "Virtual Board": store_virtual_board
936
- }
937
- current_document = doc_map.get(selected_doc_type)
938
- if current_document is None:
939
- raise dash.exceptions.PreventUpdate
940
-
941
- if selected_doc_type in spreadsheet_types:
942
  try:
943
- xlsx_bytes = markdown_tables_to_xlsx(current_document)
944
- logging.info(f"{selected_doc_type} document downloaded as Excel.")
945
- return dcc.send_bytes(xlsx_bytes.read(), f"{selected_doc_type}.xlsx")
946
- except Exception as e:
947
- logging.error(f"Error downloading {selected_doc_type} document: {str(e)}")
948
- return dcc.send_string(f"Error downloading {selected_doc_type}: {str(e)}", f"{selected_doc_type}_error.txt")
949
- else:
950
- try:
951
- plain = strip_markdown(current_document)
952
- doc = Document()
953
- for para in plain.split('\n'):
954
- doc.add_paragraph(para)
955
- output = BytesIO()
956
- doc.save(output)
957
- logging.info(f"{selected_doc_type} document downloaded as Word.")
958
- output.seek(0)
959
- return dcc.send_bytes(output.read(), f"{selected_doc_type}.docx")
960
  except Exception as e:
961
- logging.error(f"Error downloading document: {str(e)}")
962
- return dcc.send_string(f"Error downloading document: {str(e)}", f"{selected_doc_type}_error.txt")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
963
 
964
  @app.callback(
965
  Output("chat-input", "rows"),
 
452
  @app.callback(
453
  Output('file-list', 'children'),
454
  Output('store-shred', 'data'),
455
+ Output({'type': f'uploaded-doc-name-shred', 'index': ALL}, 'children'),
456
+ Output({'type': f'uploaded-doc-name-pink', 'index': ALL}, 'children'),
457
+ Output({'type': f'uploaded-doc-name-pink_review', 'index': ALL}, 'children'),
458
+ Output({'type': f'uploaded-doc-name-red', 'index': ALL}, 'children'),
459
+ Output({'type': f'uploaded-doc-name-red_review', 'index': ALL}, 'children'),
460
+ Output({'type': f'uploaded-doc-name-gold', 'index': ALL}, 'children'),
461
+ Output({'type': f'upload-doc-type-shred', 'index': ALL}, 'contents'),
462
+ Output({'type': f'upload-doc-type-pink', 'index': ALL}, 'contents'),
463
+ Output({'type': f'upload-doc-type-pink_review', 'index': ALL}, 'contents'),
464
+ Output({'type': f'upload-doc-type-red', 'index': ALL}, 'contents'),
465
+ Output({'type': f'upload-doc-type-red_review', 'index': ALL}, 'contents'),
466
+ Output({'type': f'upload-doc-type-gold', 'index': ALL}, 'contents'),
467
+ Output({'type': f'radio-doc-source-shred', 'index': ALL}, 'value'),
468
+ Output({'type': f'radio-doc-source-pink', 'index': ALL}, 'value'),
469
+ Output({'type': f'radio-doc-source-pink_review', 'index': ALL}, 'value'),
470
+ Output({'type': f'radio-doc-source-red', 'index': ALL}, 'value'),
471
+ Output({'type': f'radio-doc-source-red_review', 'index': ALL}, 'value'),
472
+ Output({'type': f'radio-doc-source-gold', 'index': ALL}, 'value'),
473
  Input('upload-document', 'contents'),
474
  State('upload-document', 'filename'),
475
  State('file-list', 'children'),
476
+ State('store-shred', 'data'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  Input({'type': 'remove-file', 'index': ALL}, 'n_clicks'),
478
  State('file-list', 'children'),
479
  State('store-shred', 'data'),
480
+ Input({'type': f'upload-doc-type-shred', 'index': ALL}, 'contents'),
481
+ State({'type': f'upload-doc-type-shred', 'index': ALL}, 'filename'),
482
+ State({'type': f'upload-doc-type-shred', 'index': ALL}, 'id'),
483
+ Input({'type': f'upload-doc-type-pink', 'index': ALL}, 'contents'),
484
+ State({'type': f'upload-doc-type-pink', 'index': ALL}, 'filename'),
485
+ State({'type': f'upload-doc-type-pink', 'index': ALL}, 'id'),
486
+ Input({'type': f'upload-doc-type-pink_review', 'index': ALL}, 'contents'),
487
+ State({'type': f'upload-doc-type-pink_review', 'index': ALL}, 'filename'),
488
+ State({'type': f'upload-doc-type-pink_review', 'index': ALL}, 'id'),
489
+ Input({'type': f'upload-doc-type-red', 'index': ALL}, 'contents'),
490
+ State({'type': f'upload-doc-type-red', 'index': ALL}, 'filename'),
491
+ State({'type': f'upload-doc-type-red', 'index': ALL}, 'id'),
492
+ Input({'type': f'upload-doc-type-red_review', 'index': ALL}, 'contents'),
493
+ State({'type': f'upload-doc-type-red_review', 'index': ALL}, 'filename'),
494
+ State({'type': f'upload-doc-type-red_review', 'index': ALL}, 'id'),
495
+ Input({'type': f'upload-doc-type-gold', 'index': ALL}, 'contents'),
496
+ State({'type': f'upload-doc-type-gold', 'index': ALL}, 'filename'),
497
+ State({'type': f'upload-doc-type-gold', 'index': ALL}, 'id'),
498
  prevent_initial_call=True
499
  )
500
+ def combined_upload_remove_update(
501
+ upload_contents, upload_filenames, existing_files, current_shred,
502
+ remove_n_clicks, remove_existing_files, remove_current_shred,
503
+ upload_shred_contents, upload_shred_filenames, upload_shred_ids,
504
+ upload_pink_contents, upload_pink_filenames, upload_pink_ids,
505
+ upload_pink_review_contents, upload_pink_review_filenames, upload_pink_review_ids,
506
+ upload_red_contents, upload_red_filenames, upload_red_ids,
507
+ upload_red_review_contents, upload_red_review_filenames, upload_red_review_ids,
508
+ upload_gold_contents, upload_gold_filenames, upload_gold_ids
509
+ ):
510
+ global uploaded_files, uploaded_doc_contents
511
+ ctx = callback_context
512
+ outputs = [dash.no_update]*21
513
+
514
+ # Handle Shred upload
515
+ if ctx.triggered and ctx.triggered[0]['prop_id'].startswith('upload-document'):
516
+ new_files = []
517
+ if upload_contents is not None:
518
+ for i, (content, name) in enumerate(zip(upload_contents, upload_filenames)):
519
+ file_content = process_document(content, name)
520
+ uploaded_files[name] = file_content
521
+ new_files.append(
522
+ dbc.Row([
523
+ dbc.Col(
524
+ html.Button('×', id={'type': 'remove-file', 'index': name}, style={'marginRight': '5px', 'fontSize': '10px'}),
525
+ width=1
526
+ ),
527
+ dbc.Col(
528
+ html.Span(name, style={'wordBreak': 'break-all'}),
529
+ width=11
530
+ )
531
+ ], id={'type': 'file-row', 'index': name}, align="center", className="mb-1")
532
+ )
533
+ if existing_files is None:
534
+ existing_files = []
535
+ elif not isinstance(existing_files, list):
536
+ existing_files = [existing_files]
537
+ logging.info("Documents uploaded and file list updated.")
538
+ outputs[0] = existing_files + new_files
539
+ outputs[1] = current_shred
540
+ return outputs
541
+
542
+ # Handle file removal
543
+ if ctx.triggered and ctx.triggered[0]['prop_id'].startswith("{\"type\":\"remove-file\""):
544
+ try:
545
+ import json
546
+ triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
547
+ triggered_dict = json.loads(triggered_id)
548
+ removed_file = triggered_dict['index']
549
+ except Exception as e:
550
+ logging.error(f"Could not parse removed file from callback context: {e}")
551
+ outputs[0] = remove_existing_files
552
+ outputs[1] = remove_current_shred
553
+ return outputs
554
+ uploaded_files.pop(removed_file, None)
555
+ logging.info(f"Removed file: {removed_file}")
556
+ filtered_files = []
557
+ if remove_existing_files:
558
+ for file in remove_existing_files:
559
+ try:
560
+ file_id = file['props']['id']
561
+ if file_id['index'] != removed_file:
562
+ filtered_files.append(file)
563
+ except Exception as e:
564
  filtered_files.append(file)
565
+ outputs[0] = filtered_files
566
+ outputs[1] = remove_current_shred
567
+ return outputs
568
+
569
+ # Handle all doc-type uploads
570
+ doc_upload_types = [
571
+ ("shred", upload_shred_contents, upload_shred_filenames, upload_shred_ids, 2, 8, 14),
572
+ ("pink", upload_pink_contents, upload_pink_filenames, upload_pink_ids, 3, 9, 15),
573
+ ("pink_review", upload_pink_review_contents, upload_pink_review_filenames, upload_pink_review_ids, 4, 10, 16),
574
+ ("red", upload_red_contents, upload_red_filenames, upload_red_ids, 5, 11, 17),
575
+ ("red_review", upload_red_review_contents, upload_red_review_filenames, upload_red_review_ids, 6, 12, 18),
576
+ ("gold", upload_gold_contents, upload_gold_filenames, upload_gold_ids, 7, 13, 19)
577
+ ]
578
+ for src, contents, filenames, ids, name_idx, content_idx, radio_idx in doc_upload_types:
579
+ if contents and any(c is not None for c in contents):
580
+ for i, content in enumerate(contents):
581
+ if content is not None:
582
+ uploaded_doc_contents[(src, ids[i]['index'])] = (content, filenames[i])
583
+ logging.info(f"{ids[i]['index']} {src} file uploaded: {filenames[i]}")
584
+ outputs[name_idx] = [filenames[i] if j==i else "" for j in range(len(contents))]
585
+ outputs[content_idx] = [content if j==i else None for j in range(len(contents))]
586
+ outputs[radio_idx] = ["uploaded" if j==i else "loaded" for j in range(len(contents))]
587
+ return outputs
588
+
589
+ return outputs
590
 
591
  def gemini_generate(prompt, max_tokens=4096, temperature=0.25):
592
  result_holder = {}
 
654
  Output('store-gold-review', 'data'),
655
  Output('store-loe', 'data'),
656
  Output('store-virtual-board', 'data'),
657
+ Output('chat-output', 'children'),
658
+ Output("download-document", "data"),
659
  Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
660
  State({'type': 'btn-generate-doc', 'index': ALL}, 'id'),
661
  State({'type': 'radio-doc-source-shred', 'index': ALL}, 'value'),
 
685
  State('store-gold-review', 'data'),
686
  State('store-loe', 'data'),
687
  State('store-virtual-board', 'data'),
688
+ Input('btn-send-chat', 'n_clicks'),
689
+ Input('btn-clear-chat', 'n_clicks'),
690
+ State('chat-input', 'value'),
691
+ State('selected-doc-type', 'data'),
692
+ State('document-preview', 'children'),
693
+ Input("btn-download", "n_clicks"),
694
+ State('selected-doc-type', 'data'),
695
+ State('store-shred', 'data'),
696
+ State('store-pink', 'data'),
697
+ State('store-pink-review', 'data'),
698
+ State('store-red', 'data'),
699
+ State('store-red-review', 'data'),
700
+ State('store-gold', 'data'),
701
+ State('store-gold-review', 'data'),
702
+ State('store-loe', 'data'),
703
+ State('store-virtual-board', 'data'),
704
  prevent_initial_call=True
705
  )
706
+ def combined_doc_chat_download(
707
  n_clicks_list, btn_ids,
708
  radio_shred, upload_shred_contents, upload_shred_filenames,
709
  radio_pink, upload_pink_contents, upload_pink_filenames,
 
711
  radio_red, upload_red_contents, upload_red_filenames,
712
  radio_red_review, upload_red_review_contents, upload_red_review_filenames,
713
  radio_gold, upload_gold_contents, upload_gold_filenames,
714
+ store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board,
715
+ btn_send, btn_clear, chat_input, chat_doc_type, doc_preview,
716
+ btn_download, dl_doc_type, dl_store_shred, dl_store_pink, dl_store_pink_review, dl_store_red, dl_store_red_review, dl_store_gold, dl_store_gold_review, dl_store_loe, dl_store_virtual_board
717
  ):
718
  ctx = callback_context
719
+ # Default outputs
720
+ outputs = [dash.no_update]*12 + [None]
721
+
722
+ # Document generation triggers
723
+ if ctx.triggered and ctx.triggered[0]['prop_id'].startswith("{\"type\":\"btn-generate-doc\""):
724
+ # Use the same logic as before for document generation
725
+ idx = [i for i, x in enumerate(n_clicks_list) if x]
726
+ if not idx:
727
+ return outputs
728
+ idx = idx[-1]
729
+ doc_type = btn_ids[idx]['index']
730
+
731
+ shred_doc = store_shred
732
+ pink_doc = store_pink
733
+ pink_review_doc = store_pink_review
734
+ red_doc = store_red
735
+ red_review_doc = store_red_review
736
+ gold_doc = store_gold
737
+ gold_review_doc = store_gold_review
738
+ loe_doc = store_loe
739
+ virtual_board_doc = store_virtual_board
740
+
741
+ def get_doc_from_radio(radio, upload_contents, upload_filenames, loaded_var):
742
+ if radio and radio[0] == 'uploaded':
743
+ if upload_contents and upload_contents[0] and upload_filenames and upload_filenames[0]:
744
+ return process_document(upload_contents[0], upload_filenames[0])
745
+ else:
746
+ return None
747
  else:
748
+ return loaded_var
 
 
749
 
750
+ if doc_type == "Shred":
751
+ if not uploaded_files:
752
+ return [html.Div("Please upload a document before shredding."), "", None, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
753
+ file_contents = list(uploaded_files.values())
754
+ try:
755
+ generated = generate_document(doc_type, file_contents)
756
+ shred_doc = generated
757
+ preview = markdown_table_preview(generated)
758
+ return [preview, "Shred generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
759
+ except Exception as e:
760
+ return [html.Div(f"Error generating document: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
 
761
 
762
+ if doc_type == "Pink":
763
+ shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
764
+ if not shred:
765
+ return [html.Div("Please provide a Shred requirements document (either loaded or uploaded) to generate Pink."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
766
+ try:
767
+ generated = generate_document(doc_type, [shred])
768
+ pink_doc = generated
769
+ preview = markdown_narrative_preview(generated)
770
+ return [preview, "Pink generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
771
+ except Exception as e:
772
+ return [html.Div(f"Error generating Pink: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
 
774
+ if doc_type == "Pink Review":
775
+ pink = get_doc_from_radio(radio_pink, upload_pink_contents, upload_pink_filenames, pink_doc)
776
+ shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
777
+ if not pink or not shred:
778
+ return [html.Div("Please provide both Pink and Shred documents (either loaded or uploaded) to generate Pink Review."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
779
+ try:
780
+ generated = generate_document(doc_type, [pink, shred])
781
+ pink_review_doc = generated
782
+ preview = markdown_table_preview(generated)
783
+ return [preview, "Pink Review generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
784
+ except Exception as e:
785
+ return [html.Div(f"Error generating Pink Review: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
786
 
787
+ if doc_type == "Red":
788
+ pink_review = get_doc_from_radio(radio_pink_review, upload_pink_review_contents, upload_pink_review_filenames, pink_review_doc)
789
+ if not pink_review:
790
+ return [html.Div("Please provide a Pink Review document (either loaded or uploaded) to generate Red."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
791
+ try:
792
+ generated = generate_document(doc_type, [pink_review])
793
+ red_doc = generated
794
+ preview = markdown_narrative_preview(generated)
795
+ return [preview, "Red generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
796
+ except Exception as e:
797
+ return [html.Div(f"Error generating Red: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
798
 
799
+ if doc_type == "Red Review":
800
+ red = get_doc_from_radio(radio_red, upload_red_contents, upload_red_filenames, red_doc)
801
+ shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
802
+ if not red or not shred:
803
+ return [html.Div("Please provide both Red and Shred documents (either loaded or uploaded) to generate Red Review."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
804
+ try:
805
+ generated = generate_document(doc_type, [red, shred])
806
+ red_review_doc = generated
807
+ preview = markdown_table_preview(generated)
808
+ return [preview, "Red Review generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
809
+ except Exception as e:
810
+ return [html.Div(f"Error generating Red Review: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
811
 
812
+ if doc_type == "Gold":
813
+ red_review = get_doc_from_radio(radio_red_review, upload_red_review_contents, upload_red_review_filenames, red_review_doc)
814
+ if not red_review:
815
+ return [html.Div("Please provide a Red Review document (either loaded or uploaded) to generate Gold."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
816
+ try:
817
+ generated = generate_document(doc_type, [red_review])
818
+ gold_doc = generated
819
+ preview = markdown_narrative_preview(generated)
820
+ return [preview, "Gold generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
821
+ except Exception as e:
822
+ return [html.Div(f"Error generating Gold: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
 
 
 
 
 
 
823
 
824
+ if doc_type == "Gold Review":
825
+ gold = get_doc_from_radio(radio_gold, upload_gold_contents, upload_gold_filenames, gold_doc)
826
+ shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
827
+ if not gold or not shred:
828
+ return [html.Div("Please provide both Gold and Shred documents (either loaded or uploaded) to generate Gold Review."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
829
+ try:
830
+ generated = generate_document(doc_type, [gold, shred])
831
+ gold_review_doc = generated
832
+ preview = markdown_table_preview(generated)
833
+ return [preview, "Gold Review generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
834
+ except Exception as e:
835
+ return [html.Div(f"Error generating Gold Review: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
836
 
837
+ if doc_type == "LOE":
838
+ gold = get_doc_from_radio(radio_gold, upload_gold_contents, upload_gold_filenames, gold_doc)
839
+ if not gold:
840
+ return [html.Div("Please provide a Gold document (either loaded or uploaded) to generate LOE."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
841
+ try:
842
+ generated = generate_document(doc_type, [gold])
843
+ loe_doc = generated
844
+ preview = markdown_table_preview(generated)
845
+ return [preview, "LOE generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
846
+ except Exception as e:
847
+ return [html.Div(f"Error generating LOE: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
848
 
849
+ if doc_type == "Virtual Board":
850
+ shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
851
+ if not shred:
852
+ return [html.Div("Please provide a Shred requirements document (either loaded or uploaded) to generate Virtual Board."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
853
+ try:
854
+ lm_text = ""
855
+ lm_match = re.search(r'(Section\s+L[\s\S]+?)(Section\s+M|$)', shred, re.IGNORECASE)
856
+ if lm_match:
857
+ lm_text = lm_match.group(1)
858
+ else:
859
+ lm_text = shred
860
+ generated = generate_document(doc_type, [lm_text])
861
+ virtual_board_doc = generated
862
+ preview = markdown_table_preview(generated)
863
+ return [preview, "Virtual Board generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
864
+ except Exception as e:
865
+ return [html.Div(f"Error generating Virtual Board: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
866
+
867
+ return [html.Div("Unsupported document type or missing required sources."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
868
+
869
+ # Chat triggers
870
+ if ctx.triggered and (ctx.triggered[0]['prop_id'] == 'btn-send-chat.n_clicks' or ctx.triggered[0]['prop_id'] == 'btn-clear-chat.n_clicks'):
871
+ if ctx.triggered[0]['prop_id'] == 'btn-clear-chat.n_clicks':
872
+ return [dash.no_update]*11 + ["", None]
873
+
874
+ doc_map = {
875
+ "Shred": store_shred,
876
+ "Pink": store_pink,
877
+ "Pink Review": store_pink_review,
878
+ "Red": store_red,
879
+ "Red Review": store_red_review,
880
+ "Gold": store_gold,
881
+ "Gold Review": store_gold_review,
882
+ "LOE": store_loe,
883
+ "Virtual Board": store_virtual_board
884
+ }
885
+ current_document = doc_map.get(chat_doc_type)
886
+
887
+ if not chat_input or current_document is None:
888
+ return [dash.no_update]*11 + ["", None]
889
+
890
+ if chat_doc_type in spreadsheet_types:
891
+ prompt = f"""Update the following {chat_doc_type} spreadsheet based on this instruction: {chat_input}
892
  Current spreadsheet (markdown table format):
893
  {current_document}
894
  Instructions:
895
  1. Provide the updated spreadsheet as a markdown table only.
896
  2. Do not include any narrative, only the markdown table.
897
+ Now, provide the updated {chat_doc_type} spreadsheet:
898
  """
899
+ else:
900
+ prompt = f"""Update the following {chat_doc_type} document based on this instruction: {chat_input}
901
  Current document:
902
  {current_document}
903
  Instructions:
904
  1. Provide the updated document content.
905
  2. Maintain proper formatting and structure.
906
  3. Incorporate the requested changes seamlessly.
907
+ 4. If the {chat_doc_type} is Pink, Red, or Gold then your goal is to write a FULL proposal response, not just a strategy and be compliant and compelling by addressing all the requirements from the document provided. Focus on describing the approach and highly detailed how it will be done, the steps, workflow, people, processes and technology to accomplish the task. Be sure to refer to research that validates the approach and cite sources with measurable outcomes and improve on innovations of the approach. Do not just say things like we will, or MicroHealth will, use active voice and verbs that are definitive in nature, not maybe, could be, should be, can be and things like that. This is a proposal response so logical flow is important so the reader can follow.
908
+ Now, provide the updated {chat_doc_type}:
909
  """
910
+ logging.info(f"Updating document via chat for {chat_doc_type} instruction: {chat_input}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
911
  try:
912
+ new_document = gemini_generate(prompt, max_tokens=4096, temperature=0.5)
913
+ if chat_doc_type in spreadsheet_types:
914
+ preview = markdown_table_preview(new_document)
915
+ else:
916
+ preview = markdown_narrative_preview(new_document)
917
+ stores = [store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board]
918
+ doc_types = ["Shred", "Pink", "Pink Review", "Red", "Red Review", "Gold", "Gold Review", "LOE", "Virtual Board"]
919
+ new_stores = []
920
+ for dt, s in zip(doc_types, stores):
921
+ if dt == chat_doc_type:
922
+ new_stores.append(new_document)
923
+ else:
924
+ new_stores.append(s)
925
+ return [preview, dash.no_update] + new_stores + ["Document updated based on: {}".format(chat_input), None]
 
 
 
926
  except Exception as e:
927
+ return [html.Div(f"Error updating document: {str(e)}"), dash.no_update, store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board, f"Error updating document: {str(e)}", None]
928
+
929
+ # Download trigger
930
+ if ctx.triggered and ctx.triggered[0]['prop_id'] == "btn-download.n_clicks":
931
+ doc_map = {
932
+ "Shred": dl_store_shred,
933
+ "Pink": dl_store_pink,
934
+ "Pink Review": dl_store_pink_review,
935
+ "Red": dl_store_red,
936
+ "Red Review": dl_store_red_review,
937
+ "Gold": dl_store_gold,
938
+ "Gold Review": dl_store_gold_review,
939
+ "LOE": dl_store_loe,
940
+ "Virtual Board": dl_store_virtual_board
941
+ }
942
+ current_document = doc_map.get(dl_doc_type)
943
+ if current_document is None:
944
+ return outputs
945
+ if dl_doc_type in spreadsheet_types:
946
+ try:
947
+ xlsx_bytes = markdown_tables_to_xlsx(current_document)
948
+ logging.info(f"{dl_doc_type} document downloaded as Excel.")
949
+ return [dash.no_update]*11 + ["", dcc.send_bytes(xlsx_bytes.read(), f"{dl_doc_type}.xlsx")]
950
+ except Exception as e:
951
+ return [dash.no_update]*11 + ["", dcc.send_string(f"Error downloading {dl_doc_type}: {str(e)}", f"{dl_doc_type}_error.txt")]
952
+ else:
953
+ try:
954
+ plain = strip_markdown(current_document)
955
+ doc = Document()
956
+ for para in plain.split('\n'):
957
+ doc.add_paragraph(para)
958
+ output = BytesIO()
959
+ doc.save(output)
960
+ logging.info(f"{dl_doc_type} document downloaded as Word.")
961
+ output.seek(0)
962
+ return [dash.no_update]*11 + ["", dcc.send_bytes(output.read(), f"{dl_doc_type}.docx")]
963
+ except Exception as e:
964
+ return [dash.no_update]*11 + ["", dcc.send_string(f"Error downloading document: {str(e)}", f"{dl_doc_type}_error.txt")]
965
+ return outputs
966
 
967
  @app.callback(
968
  Output("chat-input", "rows"),