bluenevus commited on
Commit
43e6676
·
1 Parent(s): 83acb90

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +77 -30
app.py CHANGED
@@ -16,6 +16,7 @@ import logging
16
  import uuid
17
  import xlsxwriter # Needed for Excel export engine
18
  import threading # For multi-threading
 
19
 
20
  # --- Logging Configuration ---
21
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -75,7 +76,7 @@ uploaded_pink_content = {}
75
  uploaded_red_content = {}
76
  uploaded_gold_content = {}
77
 
78
- # {session_id: {'doc': content, 'type': doc_type}} - Store the currently displayed document and its type for download/chat per session
79
  current_display_document = {}
80
 
81
  # --- Document Types ---
@@ -96,7 +97,7 @@ app.layout = dbc.Container(fluid=True, className="dbc", children=[
96
  dcc.Store(id='session-id', storage_type='session'), # Store for unique session ID
97
  # Title Row
98
  dbc.Row(
99
- dbc.Col(html.H1("Proposal AI Assistant", className="text-center my-4"), width=12) # Removed inline style
100
  ),
101
 
102
  # Progress Indicator Row
@@ -118,7 +119,7 @@ app.layout = dbc.Container(fluid=True, className="dbc", children=[
118
 
119
  # Main Content Row
120
  dbc.Row([
121
- # Left Column (Nav/Upload) - 30% width on large screens
122
  dbc.Col(
123
  dbc.Card(
124
  dbc.CardBody([
@@ -151,14 +152,16 @@ app.layout = dbc.Container(fluid=True, className="dbc", children=[
151
  ) for doc_type in document_types.keys()]
152
  ])
153
  )
154
- ])
155
- , color="light", className="h-100"), # Use bootstrap class for background, ensure full height
156
- width=12, lg=4, # Full width on small, 4/12 (33%) on large
 
 
157
  className="mb-3 mb-lg-0",
158
  style={'paddingRight': '15px'} # Add padding between columns
159
  ),
160
 
161
- # Right Column (Status/Preview/Controls/Chat) - 70% width on large screens
162
  dbc.Col(
163
  dbc.Card(
164
  dbc.CardBody([
@@ -194,9 +197,11 @@ app.layout = dbc.Container(fluid=True, className="dbc", children=[
194
  )
195
  ]), className="mb-3"
196
  )
197
- ])
198
- , className="h-100"), # Ensure full height
199
- width=12, lg=8, # Full width on small, 8/12 (67%) on large
 
 
200
  style={'paddingLeft': '15px'} # Add padding between columns
201
  )
202
  ])
@@ -214,6 +219,36 @@ def get_session_id(session_id_value=None):
214
  logging.info(f"Generated new session ID: {new_id}")
215
  return new_id
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  def process_document(contents, filename):
218
  """Processes uploaded file content (PDF or DOCX) and returns text, or None and error message."""
219
  if contents is None:
@@ -256,44 +291,56 @@ def process_document(contents, filename):
256
  logging.error(f"Error processing document {filename}: {e}", exc_info=True)
257
  return None, f"Error processing file {filename}: {str(e)}"
258
 
259
- def get_combined_uploaded_text(session_id):
260
- """Combines text content of all successfully uploaded files for a session."""
261
  with data_lock:
262
- session_files = uploaded_files.get(session_id, {})
263
  if not session_files:
264
  return ""
265
- return "\n\n--- FILE BREAK ---\n\n".join(session_files.values())
 
 
 
 
 
 
266
 
267
- def generate_ai_document(session_id, doc_type, input_docs, context_docs=None):
268
- """Generates document using Gemini AI. Updates current_display for the session."""
269
- global current_display_document # Modifying global state
270
 
 
 
271
  if not model:
272
  logging.error(f"[{session_id}] Gemini AI model not initialized.")
273
- return "Error: AI Model not configured. Please check API Key."
274
  if not input_docs or not any(doc.strip() for doc in input_docs if doc):
275
  logging.warning(f"[{session_id}] generate_ai_document called for {doc_type} with no valid input documents.")
276
- return f"Error: Missing required input document(s) for {doc_type} generation."
277
 
278
  combined_input = "\n\n---\n\n".join(filter(None, input_docs))
279
  combined_context = "\n\n---\n\n".join(filter(None, context_docs)) if context_docs else ""
280
 
281
- # Updated Prompt - Ensure it's correctly terminated
 
 
 
282
  prompt = f"""**Objective:** Generate the '{doc_type}' document.
283
- **Your Role:** Act as an expert proposal writer/analyst.
284
  **Core Instructions:**
285
- 1. **Adhere Strictly to the Task:** Generate *only* the content for the '{doc_type}'. Do not add introductions, summaries, or conversational filler unless it's part of the requested document format itself.
286
- 2. **Follow Format Guidelines:**
287
- * **Spreadsheet Types (Shred, Reviews, LOE, Board):** Structure output clearly. Use Markdown tables or a delimited format (like CSV) suitable for parsing. Define clear columns (e.g., `PWS_Section | Requirement | Finding | Recommendation` for reviews; `Section | Task | Estimated_Hours | Resource_Type` for LOE). Use '|' as the primary delimiter for tables.
288
- * **Proposal Sections (Pink, Red, Gold):** Write professional, compelling prose. Use active voice ("MicroHealth will..."). Directly address requirements from context (Shredded PWS). Detail the 'how' (technical approach, methodology, workflow, tools). Incorporate innovation and benefits (efficiency, quality, outcomes). Substantiate claims (e.g., cite Gartner, Forrester if applicable). Clearly state roles/responsibilities (labor categories). Ensure compliance with Section L/M (Evaluation Criteria) from context. Avoid vague terms ('might', 'could', 'potentially'); be assertive and confident. Use paragraphs primarily; limit bullet points to lists where essential.
289
- 3. **Utilize Provided Documents:**
290
- * **Context Document(s):** Use these as the primary reference or baseline (e.g., Shredded Requirements are the basis for compliance).
291
- * **Primary Input Document(s):** This is the main subject of the task (e.g., the PWS to be Shredded, the Pink draft to be Reviewed, the Review findings to incorporate into the next draft).
 
 
 
 
292
  **Provided Documents:**
293
- **Context Document(s) (e.g., Shredded Requirements, PWS Section L/M):**
294
  ```text
295
  {combined_context if combined_context else "N/A"}
296
  ```
297
- **Primary Input Document(s) (e.g., PWS text, Pink Draft text, Review Findings text):**
298
  ```text
299
  {combined_input}
 
16
  import uuid
17
  import xlsxwriter # Needed for Excel export engine
18
  import threading # For multi-threading
19
+ import time # For progress indicator
20
 
21
  # --- Logging Configuration ---
22
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
76
  uploaded_red_content = {}
77
  uploaded_gold_content = {}
78
 
79
+ # {session_id: {'doc': content, 'type': doc_type, 'format': format}} - Store the currently displayed document, its type, and format for download/chat per session
80
  current_display_document = {}
81
 
82
  # --- Document Types ---
 
97
  dcc.Store(id='session-id', storage_type='session'), # Store for unique session ID
98
  # Title Row
99
  dbc.Row(
100
+ dbc.Col(html.H1("Proposal AI Assistant", className="text-center my-4"), width=12)
101
  ),
102
 
103
  # Progress Indicator Row
 
119
 
120
  # Main Content Row
121
  dbc.Row([
122
+ # Left Column (Nav/Upload) - lg=4 (approx 33%)
123
  dbc.Col(
124
  dbc.Card(
125
  dbc.CardBody([
 
152
  ) for doc_type in document_types.keys()]
153
  ])
154
  )
155
+ ]),
156
+ # color="light", # Let CSS handle background
157
+ className="h-100 left-nav-card", # Add custom class for CSS targeting
158
+ ),
159
+ width=12, lg=4, # Full width on small, 4/12 on large
160
  className="mb-3 mb-lg-0",
161
  style={'paddingRight': '15px'} # Add padding between columns
162
  ),
163
 
164
+ # Right Column (Status/Preview/Controls/Chat) - lg=8 (approx 67%)
165
  dbc.Col(
166
  dbc.Card(
167
  dbc.CardBody([
 
197
  )
198
  ]), className="mb-3"
199
  )
200
+ ]),
201
+ # color="white", # Let CSS handle background
202
+ className="h-100 right-nav-card", # Add custom class for CSS targeting
203
+ ),
204
+ width=12, lg=8, # Full width on small, 8/12 on large
205
  style={'paddingLeft': '15px'} # Add padding between columns
206
  )
207
  ])
 
219
  logging.info(f"Generated new session ID: {new_id}")
220
  return new_id
221
 
222
+ def parse_generated_content(content_text):
223
+ """Attempts to parse AI-generated content into a DataFrame if it looks like a table."""
224
+ try:
225
+ # Simple check: does it contain multiple lines and pipe characters?
226
+ if content_text and '\n' in content_text and '|' in content_text:
227
+ # Try parsing as Markdown-like table (skip lines that don't fit)
228
+ lines = [line.strip() for line in content_text.strip().split('\n')]
229
+ # Remove separator lines like |---|---|
230
+ lines = [line for line in lines if not all(c in '-| ' for c in line)]
231
+ if len(lines) > 1:
232
+ # Use the first line as header, split by '|'
233
+ header = [h.strip() for h in lines[0].strip('|').split('|')]
234
+ data_rows = []
235
+ for line in lines[1:]:
236
+ values = [v.strip() for v in line.strip('|').split('|')]
237
+ if len(values) == len(header): # Ensure matching column count
238
+ data_rows.append(values)
239
+ else:
240
+ logging.warning(f"Skipping row due to mismatched columns: {line}")
241
+
242
+ if data_rows:
243
+ df = pd.DataFrame(data_rows, columns=header)
244
+ logging.info("Successfully parsed generated content as DataFrame.")
245
+ return df
246
+ except Exception as e:
247
+ logging.warning(f"Could not parse content into DataFrame: {e}. Treating as plain text.")
248
+ # If parsing fails or it doesn't look like a table, return None
249
+ logging.info("Content does not appear to be a table or parsing failed. Treating as plain text.")
250
+ return None
251
+
252
  def process_document(contents, filename):
253
  """Processes uploaded file content (PDF or DOCX) and returns text, or None and error message."""
254
  if contents is None:
 
291
  logging.error(f"Error processing document {filename}: {e}", exc_info=True)
292
  return None, f"Error processing file {filename}: {str(e)}"
293
 
294
+ def get_combined_uploaded_text(session_id, file_dict):
295
+ """Combines text content of files in the provided dictionary for a session."""
296
  with data_lock:
297
+ session_files = file_dict.get(session_id, {})
298
  if not session_files:
299
  return ""
300
+ # Combine content, adding filenames for context if multiple files
301
+ if len(session_files) > 1:
302
+ return "\n\n--- FILE BREAK ---\n\n".join(
303
+ f"**File: {fname}**\n\n{content}" for fname, content in session_files.items()
304
+ )
305
+ else:
306
+ return next(iter(session_files.values()), "")
307
 
 
 
 
308
 
309
+ def generate_ai_document(session_id, doc_type, input_docs, context_docs=None):
310
+ """Generates document using Gemini AI. Returns generated content and format ('text' or 'dataframe')."""
311
  if not model:
312
  logging.error(f"[{session_id}] Gemini AI model not initialized.")
313
+ return "Error: AI Model not configured. Please check API Key.", 'text'
314
  if not input_docs or not any(doc.strip() for doc in input_docs if doc):
315
  logging.warning(f"[{session_id}] generate_ai_document called for {doc_type} with no valid input documents.")
316
+ return f"Error: Missing required input document(s) for {doc_type} generation.", 'text'
317
 
318
  combined_input = "\n\n---\n\n".join(filter(None, input_docs))
319
  combined_context = "\n\n---\n\n".join(filter(None, context_docs)) if context_docs else ""
320
 
321
+ # Define expected output format based on doc_type
322
+ is_spreadsheet_type = doc_type in ["Shred", "Pink Review", "Red Review", "Gold Review", "LOE", "Virtual Board"]
323
+ output_format_instruction = """**Output Format:** Structure the output as a clear, parseable Markdown table. Use '|' as the column delimiter. Define meaningful column headers relevant to the task (e.g., PWS_Section, Requirement, Action_Verb for Shred; Section, Requirement, Compliance_Status, Finding, Recommendation for Reviews; Section, Task, Estimated_Hours, Resource_Type for LOE). Ensure each row corresponds to a distinct item (e.g., requirement, finding, task).""" if is_spreadsheet_type else """**Output Format:** Write professional, compelling proposal prose. Use clear paragraphs and standard formatting. Address all requirements logically. Avoid tables unless explicitly part of the proposal structure."""
324
+
325
  prompt = f"""**Objective:** Generate the '{doc_type}' document.
326
+ **Your Role:** Act as an expert proposal writer/analyst specialized in government contracting.
327
  **Core Instructions:**
328
+ 1. **Adhere Strictly to the Task:** Generate *only* the content for the '{doc_type}'. Do not add introductions, summaries, explanations, or conversational filler unless it's part of the requested document format itself (e.g., an executive summary within a proposal draft).
329
+ 2. **Follow Format Guidelines:** {output_format_instruction}
330
+ 3. **Content Requirements:**
331
+ * **Shred:** Identify requirements (explicit and implied), action verbs (shall, will, must, provide, perform, etc.), and PWS section references.
332
+ * **Proposal Sections (Pink, Red, Gold):** Write compliant and compelling content. Directly address requirements from the Context Document(s). Detail the 'how' (approach, methodology, tools). Incorporate win themes, strengths, and discriminators. Substantiate claims. Use active voice ("Our team will..."). Ensure compliance with evaluation criteria (e.g., Section L/M). Clearly map responses back to PWS requirements.
333
+ * **Reviews (Pink, Red, Gold):** Evaluate the submitted draft against the requirements (Shred/PWS) and previous review findings (if applicable). Identify compliance issues, gaps, weaknesses, and areas for improvement. Provide actionable recommendations. Be specific and reference relevant sections.
334
+ * **LOE:** Estimate the Level of Effort (hours, resource types) required to fulfill each major task or requirement identified in the Shred/PWS. Justify estimates briefly if necessary.
335
+ * **Virtual Board:** Simulate a source selection evaluation. Assess the final proposal against the PWS/Shred and evaluation criteria (Sec L/M). Assign strengths, weaknesses, deficiencies, risks. Provide a summary evaluation.
336
+ 4. **Utilize Provided Documents:**
337
+ * **Context Document(s):** These provide the baseline or reference material (e.g., Shredded Requirements, PWS Section L/M, Previous Review Findings). Refer to them diligently.
338
+ * **Primary Input Document(s):** This is the main subject of the task (e.g., the PWS text to be Shredded, the Pink draft to be Reviewed, the Red Review findings to incorporate into the Gold draft). Analyze and process this document according to the task.
339
  **Provided Documents:**
340
+ **Context Document(s):**
341
  ```text
342
  {combined_context if combined_context else "N/A"}
343
  ```
344
+ **Primary Input Document(s):**
345
  ```text
346
  {combined_input}