Spaces:
Paused
Paused
Update app.py via AI Editor
Browse files
app.py
CHANGED
@@ -3,22 +3,25 @@ import io
|
|
3 |
import os
|
4 |
import pandas as pd
|
5 |
from docx import Document
|
6 |
-
from io import BytesIO
|
7 |
import dash
|
8 |
import dash_bootstrap_components as dbc
|
9 |
from dash import html, dcc, Input, Output, State, callback_context
|
10 |
-
import google.generativeai as genai
|
11 |
from docx.shared import Pt
|
12 |
from docx.enum.style import WD_STYLE_TYPE
|
13 |
from PyPDF2 import PdfReader
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
# Initialize Dash app
|
17 |
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
|
18 |
|
19 |
-
# Configure
|
20 |
-
|
21 |
-
model = genai.GenerativeModel('gemini-2.5-pro-preview-03-25')
|
22 |
|
23 |
# Global variables
|
24 |
uploaded_files = {}
|
@@ -44,6 +47,9 @@ app.layout = dbc.Container([
|
|
44 |
dbc.Row([
|
45 |
dbc.Col([
|
46 |
html.H4("Proposal Documents", className="mt-3 mb-4"),
|
|
|
|
|
|
|
47 |
dcc.Upload(
|
48 |
id='upload-document',
|
49 |
children=html.Div([
|
@@ -75,6 +81,9 @@ app.layout = dbc.Container([
|
|
75 |
])
|
76 |
], width=3),
|
77 |
dbc.Col([
|
|
|
|
|
|
|
78 |
html.Div(id='status-bar', className="alert alert-info", style={'marginBottom': '20px'}),
|
79 |
dcc.Loading(
|
80 |
id="loading-indicator",
|
@@ -107,7 +116,7 @@ app.layout = dbc.Container([
|
|
107 |
id="chat-loading",
|
108 |
type="dot",
|
109 |
children=[
|
110 |
-
dbc.Input(id="chat-input", type="text", placeholder="Chat with AI to update document...", className="mb-2"),
|
111 |
dbc.Button("Send", id="btn-send-chat", color="primary", className="mb-3"),
|
112 |
html.Div(id="chat-output")
|
113 |
]
|
@@ -128,11 +137,14 @@ def process_document(contents, filename):
|
|
128 |
pdf = PdfReader(BytesIO(decoded))
|
129 |
text = ""
|
130 |
for page in pdf.pages:
|
131 |
-
|
|
|
|
|
132 |
return text
|
133 |
else:
|
134 |
return f"Unsupported file format: {filename}. Please upload a PDF or DOCX file."
|
135 |
except Exception as e:
|
|
|
136 |
return f"Error processing document: {str(e)}"
|
137 |
|
138 |
@app.callback(
|
@@ -155,7 +167,8 @@ def update_output(list_of_contents, list_of_names, existing_files):
|
|
155 |
]))
|
156 |
if existing_files is None:
|
157 |
existing_files = []
|
158 |
-
shredded_document = None
|
|
|
159 |
return existing_files + new_files, "Document uploaded. Please click 'Shred' to proceed."
|
160 |
return existing_files, "Please upload a document and click 'Shred' to begin."
|
161 |
|
@@ -173,7 +186,8 @@ def remove_file(n_clicks, existing_files):
|
|
173 |
raise dash.exceptions.PreventUpdate
|
174 |
removed_file = ctx.triggered[0]['prop_id'].split(',')[0].split(':')[-1].strip('}')
|
175 |
uploaded_files.pop(removed_file, None)
|
176 |
-
shredded_document = None
|
|
|
177 |
return [file for file in existing_files if file['props']['children'][1]['props']['children'] != removed_file], "Document removed. Please upload a document and click 'Shred' to begin."
|
178 |
|
179 |
def generate_document(document_type, file_contents):
|
@@ -207,8 +221,23 @@ to be sure we address them.
|
|
207 |
Now, generate the {document_type}:
|
208 |
"""
|
209 |
|
210 |
-
|
211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
|
213 |
@app.callback(
|
214 |
Output('document-preview', 'children'),
|
@@ -239,7 +268,7 @@ def generate_document_preview(*args):
|
|
239 |
shredded_document = generate_document(document_type, file_contents)
|
240 |
return dcc.Markdown(shredded_document), f"{document_type} generated", "Document shredded. You can now proceed with other operations.", {'display': 'none'}
|
241 |
except Exception as e:
|
242 |
-
|
243 |
return html.Div(f"Error generating document: {str(e)}"), "Error", "An error occurred while shredding the document.", {'display': 'none'}
|
244 |
|
245 |
if shredded_document is None:
|
@@ -262,9 +291,10 @@ def generate_document_preview(*args):
|
|
262 |
if document_type == "Pink Review":
|
263 |
pink_review_document = current_document
|
264 |
|
|
|
265 |
return dcc.Markdown(current_document), f"{document_type} generated", f"{document_type} document generated successfully.", {'display': 'none'}
|
266 |
except Exception as e:
|
267 |
-
|
268 |
return html.Div(f"Error generating document: {str(e)}"), "Error", "An error occurred while generating the document.", {'display': 'none'}
|
269 |
|
270 |
@app.callback(
|
@@ -274,6 +304,7 @@ def generate_document_preview(*args):
|
|
274 |
)
|
275 |
def update_pink_review_filename(contents, filename):
|
276 |
if contents is not None:
|
|
|
277 |
return filename
|
278 |
return ""
|
279 |
|
@@ -298,11 +329,24 @@ Instructions:
|
|
298 |
3. Incorporate the requested changes seamlessly.
|
299 |
Now, provide the updated {document_type}:
|
300 |
"""
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
306 |
|
307 |
@app.callback(
|
308 |
Output("download-document", "data"),
|
@@ -315,27 +359,29 @@ def download_document(n_clicks):
|
|
315 |
raise dash.exceptions.PreventUpdate
|
316 |
|
317 |
if document_type == "LOE":
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
|
|
327 |
else:
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
|
|
337 |
|
338 |
if __name__ == '__main__':
|
339 |
print("Starting the Dash application...")
|
340 |
-
app.run(debug=
|
341 |
print("Dash application has finished running.")
|
|
|
3 |
import os
|
4 |
import pandas as pd
|
5 |
from docx import Document
|
6 |
+
from io import BytesIO, StringIO
|
7 |
import dash
|
8 |
import dash_bootstrap_components as dbc
|
9 |
from dash import html, dcc, Input, Output, State, callback_context
|
|
|
10 |
from docx.shared import Pt
|
11 |
from docx.enum.style import WD_STYLE_TYPE
|
12 |
from PyPDF2 import PdfReader
|
13 |
+
import openai
|
14 |
+
import logging
|
15 |
+
import threading
|
16 |
+
|
17 |
+
# Logging configuration
|
18 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
|
19 |
|
20 |
# Initialize Dash app
|
21 |
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
|
22 |
|
23 |
+
# Configure OpenAI
|
24 |
+
openai.api_key = os.environ.get("OPENAI_API_KEY", "")
|
|
|
25 |
|
26 |
# Global variables
|
27 |
uploaded_files = {}
|
|
|
47 |
dbc.Row([
|
48 |
dbc.Col([
|
49 |
html.H4("Proposal Documents", className="mt-3 mb-4"),
|
50 |
+
html.Div([
|
51 |
+
html.Div(className="blinking-dot", style={'margin':'0 auto','width':'16px','height':'16px'}),
|
52 |
+
], style={'textAlign':'center', 'marginBottom':'10px'}),
|
53 |
dcc.Upload(
|
54 |
id='upload-document',
|
55 |
children=html.Div([
|
|
|
81 |
])
|
82 |
], width=3),
|
83 |
dbc.Col([
|
84 |
+
html.Div([
|
85 |
+
html.Div(className="blinking-dot", style={'margin':'0 auto','width':'16px','height':'16px'}),
|
86 |
+
], style={'textAlign':'center', 'marginBottom':'10px'}),
|
87 |
html.Div(id='status-bar', className="alert alert-info", style={'marginBottom': '20px'}),
|
88 |
dcc.Loading(
|
89 |
id="loading-indicator",
|
|
|
116 |
id="chat-loading",
|
117 |
type="dot",
|
118 |
children=[
|
119 |
+
dbc.Input(id="chat-input", type="text", placeholder="Chat with AI to update document...", className="mb-2", style={'whiteSpace':'pre-wrap'}),
|
120 |
dbc.Button("Send", id="btn-send-chat", color="primary", className="mb-3"),
|
121 |
html.Div(id="chat-output")
|
122 |
]
|
|
|
137 |
pdf = PdfReader(BytesIO(decoded))
|
138 |
text = ""
|
139 |
for page in pdf.pages:
|
140 |
+
page_text = page.extract_text()
|
141 |
+
if page_text:
|
142 |
+
text += page_text
|
143 |
return text
|
144 |
else:
|
145 |
return f"Unsupported file format: {filename}. Please upload a PDF or DOCX file."
|
146 |
except Exception as e:
|
147 |
+
logging.error(f"Error processing document: {str(e)}")
|
148 |
return f"Error processing document: {str(e)}"
|
149 |
|
150 |
@app.callback(
|
|
|
167 |
]))
|
168 |
if existing_files is None:
|
169 |
existing_files = []
|
170 |
+
shredded_document = None
|
171 |
+
logging.info("Documents uploaded and file list updated.")
|
172 |
return existing_files + new_files, "Document uploaded. Please click 'Shred' to proceed."
|
173 |
return existing_files, "Please upload a document and click 'Shred' to begin."
|
174 |
|
|
|
186 |
raise dash.exceptions.PreventUpdate
|
187 |
removed_file = ctx.triggered[0]['prop_id'].split(',')[0].split(':')[-1].strip('}')
|
188 |
uploaded_files.pop(removed_file, None)
|
189 |
+
shredded_document = None
|
190 |
+
logging.info(f"Removed file: {removed_file}")
|
191 |
return [file for file in existing_files if file['props']['children'][1]['props']['children'] != removed_file], "Document removed. Please upload a document and click 'Shred' to begin."
|
192 |
|
193 |
def generate_document(document_type, file_contents):
|
|
|
221 |
Now, generate the {document_type}:
|
222 |
"""
|
223 |
|
224 |
+
logging.info(f"Generating document for type: {document_type}")
|
225 |
+
try:
|
226 |
+
response = openai.ChatCompletion.create(
|
227 |
+
model="gpt-4-1106-preview",
|
228 |
+
messages=[
|
229 |
+
{"role": "system", "content": "You are a helpful, expert government proposal writer."},
|
230 |
+
{"role": "user", "content": prompt}
|
231 |
+
],
|
232 |
+
max_tokens=4096,
|
233 |
+
temperature=0.25,
|
234 |
+
)
|
235 |
+
generated_text = response['choices'][0]['message']['content']
|
236 |
+
logging.info("Document generated successfully.")
|
237 |
+
return generated_text
|
238 |
+
except Exception as e:
|
239 |
+
logging.error(f"Error generating document: {str(e)}")
|
240 |
+
raise
|
241 |
|
242 |
@app.callback(
|
243 |
Output('document-preview', 'children'),
|
|
|
268 |
shredded_document = generate_document(document_type, file_contents)
|
269 |
return dcc.Markdown(shredded_document), f"{document_type} generated", "Document shredded. You can now proceed with other operations.", {'display': 'none'}
|
270 |
except Exception as e:
|
271 |
+
logging.error(f"Error generating document: {str(e)}")
|
272 |
return html.Div(f"Error generating document: {str(e)}"), "Error", "An error occurred while shredding the document.", {'display': 'none'}
|
273 |
|
274 |
if shredded_document is None:
|
|
|
291 |
if document_type == "Pink Review":
|
292 |
pink_review_document = current_document
|
293 |
|
294 |
+
logging.info(f"{document_type} document generated successfully.")
|
295 |
return dcc.Markdown(current_document), f"{document_type} generated", f"{document_type} document generated successfully.", {'display': 'none'}
|
296 |
except Exception as e:
|
297 |
+
logging.error(f"Error generating document: {str(e)}")
|
298 |
return html.Div(f"Error generating document: {str(e)}"), "Error", "An error occurred while generating the document.", {'display': 'none'}
|
299 |
|
300 |
@app.callback(
|
|
|
304 |
)
|
305 |
def update_pink_review_filename(contents, filename):
|
306 |
if contents is not None:
|
307 |
+
logging.info(f"Pink Review file uploaded: {filename}")
|
308 |
return filename
|
309 |
return ""
|
310 |
|
|
|
329 |
3. Incorporate the requested changes seamlessly.
|
330 |
Now, provide the updated {document_type}:
|
331 |
"""
|
332 |
+
|
333 |
+
logging.info(f"Updating document via chat for {document_type} instruction: {chat_input}")
|
334 |
+
try:
|
335 |
+
response = openai.ChatCompletion.create(
|
336 |
+
model="gpt-4-1106-preview",
|
337 |
+
messages=[
|
338 |
+
{"role": "system", "content": "You are a helpful, expert government proposal writer."},
|
339 |
+
{"role": "user", "content": prompt}
|
340 |
+
],
|
341 |
+
max_tokens=4096,
|
342 |
+
temperature=0.2,
|
343 |
+
)
|
344 |
+
current_document = response['choices'][0]['message']['content']
|
345 |
+
logging.info("Document updated via chat successfully.")
|
346 |
+
return f"Document updated based on: {chat_input}", dcc.Markdown(current_document)
|
347 |
+
except Exception as e:
|
348 |
+
logging.error(f"Error updating document via chat: {str(e)}")
|
349 |
+
return f"Error updating document: {str(e)}", html.Div(f"Error updating document: {str(e)}")
|
350 |
|
351 |
@app.callback(
|
352 |
Output("download-document", "data"),
|
|
|
359 |
raise dash.exceptions.PreventUpdate
|
360 |
|
361 |
if document_type == "LOE":
|
362 |
+
try:
|
363 |
+
df = pd.read_csv(StringIO(current_document))
|
364 |
+
output = BytesIO()
|
365 |
+
with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
|
366 |
+
df.to_excel(writer, sheet_name='LOE', index=False)
|
367 |
+
logging.info("LOE document downloaded as Excel.")
|
368 |
+
return dcc.send_bytes(output.getvalue(), f"{document_type}.xlsx")
|
369 |
+
except Exception as e:
|
370 |
+
logging.error(f"Error downloading LOE document: {str(e)}")
|
371 |
+
return dcc.send_string(f"Error downloading LOE: {str(e)}", f"{document_type}_error.txt")
|
372 |
else:
|
373 |
+
try:
|
374 |
+
doc = Document()
|
375 |
+
doc.add_paragraph(current_document)
|
376 |
+
output = BytesIO()
|
377 |
+
doc.save(output)
|
378 |
+
logging.info("Document downloaded as Word.")
|
379 |
+
return dcc.send_bytes(output.getvalue(), f"{document_type}.docx")
|
380 |
+
except Exception as e:
|
381 |
+
logging.error(f"Error downloading document: {str(e)}")
|
382 |
+
return dcc.send_string(f"Error downloading document: {str(e)}", f"{document_type}_error.txt")
|
383 |
|
384 |
if __name__ == '__main__':
|
385 |
print("Starting the Dash application...")
|
386 |
+
app.run(debug=True, host='0.0.0.0', port=7860, threaded=True)
|
387 |
print("Dash application has finished running.")
|