bluenevus commited on
Commit
e1f88ac
·
1 Parent(s): 906205b

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +84 -40
app.py CHANGED
@@ -12,6 +12,7 @@ from threading import Lock
12
  import tempfile
13
  import shutil
14
  import uuid
 
15
 
16
  import google.generativeai as genai
17
 
@@ -33,7 +34,6 @@ MAX_OUTPUT_TOKENS = 65536
33
  SESSION_STORE = {}
34
 
35
  def get_session_id_from_cookie(cookie_str):
36
- # Parse a cookie string like "dash_session=abcd; something=xyz"
37
  if not cookie_str:
38
  return None
39
  for part in cookie_str.split(";"):
@@ -42,13 +42,10 @@ def get_session_id_from_cookie(cookie_str):
42
  return None
43
 
44
  def get_session_id(session_id=None):
45
- # Always require session_id as input; never generate unless absent
46
  if session_id and session_id in SESSION_STORE:
47
  return session_id
48
  if session_id:
49
- # New session, not yet in store
50
  return session_id
51
- # Defensive fallback: generate a new session_id (should never hit this)
52
  sid = str(uuid.uuid4())
53
  return sid
54
 
@@ -157,6 +154,39 @@ def gemini_generate_content(prompt, file_id=None, chat_input=None, file_ids=None
157
  logging.error("Error during Gemini generate_content: %s", e)
158
  return f"Error during Gemini completion: {e}"
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  def save_shredded_as_docx(shredded_text, rfp_filename):
161
  doc = Document()
162
  doc.add_heading(f"Shredded Requirements for {rfp_filename}", 0)
@@ -237,11 +267,11 @@ def process_document(sess_data, action, selected_filename=None, chat_input=None,
237
  prompt += doc_content
238
  result = gemini_generate_content(prompt, file_id=doc_fileid, chat_input=chat_input)
239
  if result and not result.startswith("Error"):
240
- docx_bytes = save_shredded_as_docx(result, selected_filename)
241
- generated_docx_name = f"{os.path.splitext(selected_filename)[0]}_shredded.docx"
242
- sess_data["uploaded_documents"][generated_docx_name] = result
243
- sess_data["shredded_documents"][generated_docx_name] = docx_bytes
244
- return result, generated_docx_name, docx_bytes, None, None
245
  else:
246
  return result, None, None, None, None
247
 
@@ -268,11 +298,11 @@ def process_document(sess_data, action, selected_filename=None, chat_input=None,
268
  )
269
  result = gemini_generate_content(prompt, file_id=None, chat_input=None)
270
  if result and not result.startswith("Error"):
271
- docx_bytes = save_compliance_as_docx(result, selected_filename)
272
- compliance_docx_name = f"{os.path.splitext(selected_filename)[0]}_compliance_check.docx"
273
- sess_data["uploaded_documents"][compliance_docx_name] = result
274
- sess_data["shredded_documents"][compliance_docx_name] = docx_bytes
275
- return result, compliance_docx_name, docx_bytes, None, None
276
  else:
277
  return result, None, None, None, None
278
 
@@ -298,11 +328,11 @@ def process_document(sess_data, action, selected_filename=None, chat_input=None,
298
  )
299
  result = gemini_generate_content(prompt, file_id=None, chat_input=None)
300
  if result and not result.startswith("Error"):
301
- docx_bytes = save_virtual_board_as_docx(result, selected_filename)
302
- board_docx_name = f"{os.path.splitext(selected_filename)[0]}_evaluation_board.docx"
303
- sess_data["uploaded_documents"][board_docx_name] = result
304
- sess_data["shredded_documents"][board_docx_name] = docx_bytes
305
- return result, board_docx_name, docx_bytes, None, None
306
  else:
307
  return result, None, None, None, None
308
 
@@ -402,12 +432,12 @@ def process_document(sess_data, action, selected_filename=None, chat_input=None,
402
  prompt += f"\n---\nProposal Document ({selected_proposal_filename}):\n{proposal_text}\n"
403
  result = gemini_generate_content(prompt, file_id=None, chat_input=chat_input)
404
  if result and not result.startswith("Error"):
405
- loe_docx_name = f"{proposal_base_name}_loe.docx"
406
- sess_data["proposals"][loe_docx_name] = result
407
- sess_data["proposals_fileid"][loe_docx_name] = None
408
- docx_bytes = save_loe_as_docx(result, proposal_base_name)
409
- logging.info(f"LOE generated and saved as {loe_docx_name}")
410
- return result, None, None, loe_docx_name, docx_bytes
411
  else:
412
  return result, None, None, None, None
413
 
@@ -417,7 +447,7 @@ def get_documents_list(docdict, shreddedict):
417
  all_docs = {}
418
  for filename, text in docdict.items():
419
  all_docs[filename] = text
420
- for filename, docx_bytes in shreddedict.items():
421
  if filename not in all_docs:
422
  all_docs[filename] = None
423
  if not all_docs:
@@ -425,12 +455,16 @@ def get_documents_list(docdict, shreddedict):
425
  doc_list = []
426
  for filename in all_docs:
427
  truncated = truncate_filename(filename)
428
- if filename.lower().endswith('.docx') and filename in shreddedict:
 
 
 
 
429
  b64 = base64.b64encode(shreddedict[filename]).decode('utf-8')
430
  mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
431
  else:
432
  content = docdict.get(filename, "")
433
- b64 = base64.b64encode(content.encode('utf-8')).decode('utf-8')
434
  mime = "text/plain"
435
  download_link = html.A(
436
  truncated,
@@ -453,14 +487,21 @@ def get_proposals_list(proposaldict):
453
  doc_list = []
454
  for filename in proposaldict:
455
  truncated = truncate_filename(filename)
 
456
  file_content = proposaldict[filename]
457
  try:
458
- if filename.lower().endswith('_loe.docx'):
 
 
 
 
459
  docx_bytes = save_loe_as_docx(file_content, filename)
 
 
460
  else:
461
  docx_bytes = save_proposal_as_docx(file_content, filename)
462
- b64 = base64.b64encode(docx_bytes).decode('utf-8')
463
- mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
464
  except Exception:
465
  b64 = base64.b64encode(file_content.encode('utf-8')).decode('utf-8')
466
  mime = "text/plain"
@@ -481,9 +522,9 @@ def get_proposals_list(proposaldict):
481
 
482
  app.layout = dbc.Container([
483
  dcc.Store(id='preview-window-state', data='expanded'),
484
- dcc.Store(id='session-id-store', storage_type='session'), # Session ID per browser session
485
- html.Div(id='set-session-cookie', style={'display': 'none'}), # dummy div for JS callback
486
- dcc.Location(id='dummy-url', refresh=False), # For JS init on page load
487
  dbc.Row([
488
  dbc.Col([
489
  dbc.Card([
@@ -572,6 +613,11 @@ app.layout = dbc.Container([
572
  children=html.Div(
573
  id="output-preview-container",
574
  children=html.Div(id="output-data-upload"),
 
 
 
 
 
575
  ),
576
  style={"textAlign": "center"}
577
  )
@@ -581,7 +627,6 @@ app.layout = dbc.Container([
581
  ], style={'marginTop':'20px'})
582
  ], fluid=True)
583
 
584
- # JS callback: Set session cookie and session-id-store on first page load if not present
585
  app.clientside_callback(
586
  """
587
  function(n, dummy_url) {
@@ -662,7 +707,6 @@ def master_callback(
662
  chat_input, cancel_clicks, preview_window_state,
663
  session_id
664
  ):
665
- # Always get session_id from dcc.Store, never generate
666
  sid = get_session_id(session_id)
667
  sess_data = get_session_data(sid)
668
  ctx = callback_context
@@ -793,17 +837,17 @@ def master_callback(
793
  try:
794
  if triggered_id == "shred-action-btn":
795
  action_name = "shred"
796
- result, generated_filename, generated_docx_bytes, _, _ = process_document(sess_data, action_name, doc_value, chat_input, uploaded_rfp_decoded_bytes, None)
797
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
798
  elif triggered_id == "compliance-action-btn":
799
  action_name = "compliance"
800
- result, generated_filename, generated_docx_bytes, _, _ = process_document(
801
  sess_data, action_name, doc_value, chat_input, uploaded_rfp_decoded_bytes, proposal_value
802
  )
803
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
804
  elif triggered_id == "board-action-btn":
805
  action_name = "virtual_board"
806
- result, generated_filename, generated_docx_bytes, _, _ = process_document(
807
  sess_data, action_name, doc_value, chat_input, uploaded_rfp_decoded_bytes, proposal_value
808
  )
809
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
@@ -822,7 +866,7 @@ def master_callback(
822
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
823
  elif triggered_id == "loe-action-btn":
824
  action_name = "loe"
825
- result, _, _, generated_filename, generated_docx_bytes = process_document(
826
  sess_data, action_name, None, chat_input, None, proposal_value
827
  )
828
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
 
12
  import tempfile
13
  import shutil
14
  import uuid
15
+ import re
16
 
17
  import google.generativeai as genai
18
 
 
34
  SESSION_STORE = {}
35
 
36
  def get_session_id_from_cookie(cookie_str):
 
37
  if not cookie_str:
38
  return None
39
  for part in cookie_str.split(";"):
 
42
  return None
43
 
44
  def get_session_id(session_id=None):
 
45
  if session_id and session_id in SESSION_STORE:
46
  return session_id
47
  if session_id:
 
48
  return session_id
 
49
  sid = str(uuid.uuid4())
50
  return sid
51
 
 
154
  logging.error("Error during Gemini generate_content: %s", e)
155
  return f"Error during Gemini completion: {e}"
156
 
157
+ def parse_markdown_table(md):
158
+ lines = md.split('\n')
159
+ table_lines = []
160
+ in_table = False
161
+ for l in lines:
162
+ if l.strip().startswith('|') and l.strip().endswith('|'):
163
+ table_lines.append(l.strip())
164
+ in_table = True
165
+ elif in_table and l.strip() == '':
166
+ break
167
+ if not table_lines:
168
+ raise ValueError("No markdown table found")
169
+ header = table_lines[0].strip('|').split('|')
170
+ header = [h.strip() for h in header]
171
+ rows = []
172
+ for l in table_lines[2:]:
173
+ r = [c.strip() for c in l.strip('|').split('|')]
174
+ if len(r) == len(header):
175
+ rows.append(r)
176
+ df = pd.DataFrame(rows, columns=header)
177
+ return df
178
+
179
+ def save_markdown_as_xlsx(md_text, base_filename):
180
+ try:
181
+ df = parse_markdown_table(md_text)
182
+ memf = io.BytesIO()
183
+ df.to_excel(memf, index=False, engine='xlsxwriter')
184
+ memf.seek(0)
185
+ return memf.read()
186
+ except Exception as e:
187
+ logging.error(f"Failed to convert markdown to XLSX for {base_filename}: {e}")
188
+ return None
189
+
190
  def save_shredded_as_docx(shredded_text, rfp_filename):
191
  doc = Document()
192
  doc.add_heading(f"Shredded Requirements for {rfp_filename}", 0)
 
267
  prompt += doc_content
268
  result = gemini_generate_content(prompt, file_id=doc_fileid, chat_input=chat_input)
269
  if result and not result.startswith("Error"):
270
+ xlsx_bytes = save_markdown_as_xlsx(result, selected_filename)
271
+ generated_xlsx_name = f"{os.path.splitext(selected_filename)[0]}_shredded.xlsx"
272
+ sess_data["uploaded_documents"][generated_xlsx_name] = result
273
+ sess_data["shredded_documents"][generated_xlsx_name] = xlsx_bytes
274
+ return result, generated_xlsx_name, xlsx_bytes, None, None
275
  else:
276
  return result, None, None, None, None
277
 
 
298
  )
299
  result = gemini_generate_content(prompt, file_id=None, chat_input=None)
300
  if result and not result.startswith("Error"):
301
+ xlsx_bytes = save_markdown_as_xlsx(result, selected_filename)
302
+ compliance_xlsx_name = f"{os.path.splitext(selected_filename)[0]}_compliance_check.xlsx"
303
+ sess_data["uploaded_documents"][compliance_xlsx_name] = result
304
+ sess_data["shredded_documents"][compliance_xlsx_name] = xlsx_bytes
305
+ return result, compliance_xlsx_name, xlsx_bytes, None, None
306
  else:
307
  return result, None, None, None, None
308
 
 
328
  )
329
  result = gemini_generate_content(prompt, file_id=None, chat_input=None)
330
  if result and not result.startswith("Error"):
331
+ xlsx_bytes = save_markdown_as_xlsx(result, selected_filename)
332
+ board_xlsx_name = f"{os.path.splitext(selected_filename)[0]}_evaluation_board.xlsx"
333
+ sess_data["uploaded_documents"][board_xlsx_name] = result
334
+ sess_data["shredded_documents"][board_xlsx_name] = xlsx_bytes
335
+ return result, board_xlsx_name, xlsx_bytes, None, None
336
  else:
337
  return result, None, None, None, None
338
 
 
432
  prompt += f"\n---\nProposal Document ({selected_proposal_filename}):\n{proposal_text}\n"
433
  result = gemini_generate_content(prompt, file_id=None, chat_input=chat_input)
434
  if result and not result.startswith("Error"):
435
+ loe_xlsx_name = f"{proposal_base_name}_loe.xlsx"
436
+ sess_data["proposals"][loe_xlsx_name] = result
437
+ sess_data["proposals_fileid"][loe_xlsx_name] = None
438
+ xlsx_bytes = save_markdown_as_xlsx(result, proposal_base_name)
439
+ logging.info(f"LOE generated and saved as {loe_xlsx_name}")
440
+ return result, None, None, loe_xlsx_name, xlsx_bytes
441
  else:
442
  return result, None, None, None, None
443
 
 
447
  all_docs = {}
448
  for filename, text in docdict.items():
449
  all_docs[filename] = text
450
+ for filename, doc_bytes in shreddedict.items():
451
  if filename not in all_docs:
452
  all_docs[filename] = None
453
  if not all_docs:
 
455
  doc_list = []
456
  for filename in all_docs:
457
  truncated = truncate_filename(filename)
458
+ ext = filename.lower().split('.')[-1]
459
+ if ext == "xlsx" and filename in shreddedict:
460
+ b64 = base64.b64encode(shreddedict[filename]).decode('utf-8')
461
+ mime = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
462
+ elif ext == "docx" and filename in shreddedict:
463
  b64 = base64.b64encode(shreddedict[filename]).decode('utf-8')
464
  mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
465
  else:
466
  content = docdict.get(filename, "")
467
+ b64 = base64.b64encode((content.encode('utf-8') if isinstance(content, str) else b"")).decode('utf-8')
468
  mime = "text/plain"
469
  download_link = html.A(
470
  truncated,
 
487
  doc_list = []
488
  for filename in proposaldict:
489
  truncated = truncate_filename(filename)
490
+ ext = filename.lower().split('.')[-1]
491
  file_content = proposaldict[filename]
492
  try:
493
+ if ext == "xlsx":
494
+ from io import BytesIO
495
+ b64 = base64.b64encode(save_markdown_as_xlsx(file_content, filename)).decode('utf-8')
496
+ mime = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
497
+ elif filename.lower().endswith('_loe.docx'):
498
  docx_bytes = save_loe_as_docx(file_content, filename)
499
+ b64 = base64.b64encode(docx_bytes).decode('utf-8')
500
+ mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
501
  else:
502
  docx_bytes = save_proposal_as_docx(file_content, filename)
503
+ b64 = base64.b64encode(docx_bytes).decode('utf-8')
504
+ mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
505
  except Exception:
506
  b64 = base64.b64encode(file_content.encode('utf-8')).decode('utf-8')
507
  mime = "text/plain"
 
522
 
523
  app.layout = dbc.Container([
524
  dcc.Store(id='preview-window-state', data='expanded'),
525
+ dcc.Store(id='session-id-store', storage_type='session'),
526
+ html.Div(id='set-session-cookie', style={'display': 'none'}),
527
+ dcc.Location(id='dummy-url', refresh=False),
528
  dbc.Row([
529
  dbc.Col([
530
  dbc.Card([
 
613
  children=html.Div(
614
  id="output-preview-container",
615
  children=html.Div(id="output-data-upload"),
616
+ style={
617
+ "height": "300px", # Adjust this value as needed to triple the original height
618
+ "overflowY": "auto", # Adds vertical scrollbar when content exceeds height
619
+ "overflowX": "auto", # Adds horizontal scrollbar if needed
620
+ }
621
  ),
622
  style={"textAlign": "center"}
623
  )
 
627
  ], style={'marginTop':'20px'})
628
  ], fluid=True)
629
 
 
630
  app.clientside_callback(
631
  """
632
  function(n, dummy_url) {
 
707
  chat_input, cancel_clicks, preview_window_state,
708
  session_id
709
  ):
 
710
  sid = get_session_id(session_id)
711
  sess_data = get_session_data(sid)
712
  ctx = callback_context
 
837
  try:
838
  if triggered_id == "shred-action-btn":
839
  action_name = "shred"
840
+ result, generated_filename, generated_xlsx_bytes, _, _ = process_document(sess_data, action_name, doc_value, chat_input, uploaded_rfp_decoded_bytes, None)
841
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
842
  elif triggered_id == "compliance-action-btn":
843
  action_name = "compliance"
844
+ result, generated_filename, generated_xlsx_bytes, _, _ = process_document(
845
  sess_data, action_name, doc_value, chat_input, uploaded_rfp_decoded_bytes, proposal_value
846
  )
847
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
848
  elif triggered_id == "board-action-btn":
849
  action_name = "virtual_board"
850
+ result, generated_filename, generated_xlsx_bytes, _, _ = process_document(
851
  sess_data, action_name, doc_value, chat_input, uploaded_rfp_decoded_bytes, proposal_value
852
  )
853
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
 
866
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
867
  elif triggered_id == "loe-action-btn":
868
  action_name = "loe"
869
+ result, _, _, generated_filename, generated_xlsx_bytes = process_document(
870
  sess_data, action_name, None, chat_input, None, proposal_value
871
  )
872
  output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})