Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
@@ -24,6 +24,8 @@ model = genai.GenerativeModel('gemini-2.5-pro-preview-03-25')
|
|
24 |
uploaded_files = {}
|
25 |
current_document = None
|
26 |
document_type = None
|
|
|
|
|
27 |
|
28 |
# Document types and their descriptions
|
29 |
document_types = {
|
@@ -73,7 +75,7 @@ app.layout = dbc.Container([
|
|
73 |
])
|
74 |
], width=3),
|
75 |
dbc.Col([
|
76 |
-
html.Div(style={
|
77 |
dcc.Loading(
|
78 |
id="loading-indicator",
|
79 |
type="dot",
|
@@ -83,7 +85,24 @@ app.layout = dbc.Container([
|
|
83 |
dbc.Button("Download Document", id="btn-download", color="success", className="mt-3"),
|
84 |
dcc.Download(id="download-document"),
|
85 |
html.Hr(),
|
86 |
-
html.Div(style={
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
dcc.Loading(
|
88 |
id="chat-loading",
|
89 |
type="dot",
|
@@ -118,12 +137,13 @@ def process_document(contents, filename):
|
|
118 |
|
119 |
@app.callback(
|
120 |
Output('file-list', 'children'),
|
|
|
121 |
Input('upload-document', 'contents'),
|
122 |
State('upload-document', 'filename'),
|
123 |
State('file-list', 'children')
|
124 |
)
|
125 |
def update_output(list_of_contents, list_of_names, existing_files):
|
126 |
-
global uploaded_files
|
127 |
if list_of_contents is not None:
|
128 |
new_files = []
|
129 |
for i, (content, name) in enumerate(zip(list_of_contents, list_of_names)):
|
@@ -135,23 +155,26 @@ def update_output(list_of_contents, list_of_names, existing_files):
|
|
135 |
]))
|
136 |
if existing_files is None:
|
137 |
existing_files = []
|
138 |
-
|
139 |
-
|
|
|
140 |
|
141 |
@app.callback(
|
142 |
Output('file-list', 'children', allow_duplicate=True),
|
|
|
143 |
Input({'type': 'remove-file', 'index': dash.ALL}, 'n_clicks'),
|
144 |
State('file-list', 'children'),
|
145 |
prevent_initial_call=True
|
146 |
)
|
147 |
def remove_file(n_clicks, existing_files):
|
148 |
-
global uploaded_files
|
149 |
ctx = dash.callback_context
|
150 |
if not ctx.triggered:
|
151 |
raise dash.exceptions.PreventUpdate
|
152 |
removed_file = ctx.triggered[0]['prop_id'].split(',')[0].split(':')[-1].strip('}')
|
153 |
uploaded_files.pop(removed_file, None)
|
154 |
-
|
|
|
155 |
|
156 |
def generate_document(document_type, file_contents):
|
157 |
prompt = f"""Generate a {document_type} based on the following project artifacts:
|
@@ -170,28 +193,69 @@ Now, generate the {document_type}:
|
|
170 |
@app.callback(
|
171 |
Output('document-preview', 'children'),
|
172 |
Output('loading-output', 'children'),
|
|
|
|
|
173 |
[Input(f'btn-{doc_type.lower().replace("_", "-")}', 'n_clicks') for doc_type in document_types.keys()],
|
|
|
174 |
prevent_initial_call=True
|
175 |
)
|
176 |
def generate_document_preview(*args):
|
177 |
-
global current_document, document_type
|
178 |
ctx = dash.callback_context
|
179 |
if not ctx.triggered:
|
180 |
raise dash.exceptions.PreventUpdate
|
181 |
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
|
182 |
document_type = button_id.replace('btn-', '').replace('-', '_').title()
|
183 |
-
|
184 |
-
|
185 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
186 |
|
187 |
-
file_contents = list(uploaded_files.values())
|
188 |
-
|
189 |
try:
|
190 |
-
|
191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
except Exception as e:
|
193 |
print(f"Error generating document: {str(e)}")
|
194 |
-
return html.Div(f"Error generating document: {str(e)}"), "Error"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
195 |
|
196 |
@app.callback(
|
197 |
Output('chat-output', 'children'),
|
@@ -230,15 +294,26 @@ def download_document(n_clicks):
|
|
230 |
if current_document is None:
|
231 |
raise dash.exceptions.PreventUpdate
|
232 |
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
242 |
|
243 |
if __name__ == '__main__':
|
244 |
print("Starting the Dash application...")
|
|
|
24 |
uploaded_files = {}
|
25 |
current_document = None
|
26 |
document_type = None
|
27 |
+
shredded_document = None
|
28 |
+
pink_review_document = None
|
29 |
|
30 |
# Document types and their descriptions
|
31 |
document_types = {
|
|
|
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",
|
81 |
type="dot",
|
|
|
85 |
dbc.Button("Download Document", id="btn-download", color="success", className="mt-3"),
|
86 |
dcc.Download(id="download-document"),
|
87 |
html.Hr(),
|
88 |
+
html.Div(id='pink-review-upload', style={'display': 'none'}, children=[
|
89 |
+
dcc.Upload(
|
90 |
+
id='upload-pink-review',
|
91 |
+
children=html.Div(['Drag and Drop or ', html.A('Select Pink Review File')]),
|
92 |
+
style={
|
93 |
+
'width': '100%',
|
94 |
+
'height': '60px',
|
95 |
+
'lineHeight': '60px',
|
96 |
+
'borderWidth': '1px',
|
97 |
+
'borderStyle': 'dashed',
|
98 |
+
'borderRadius': '5px',
|
99 |
+
'textAlign': 'center',
|
100 |
+
'margin': '10px 0'
|
101 |
+
},
|
102 |
+
multiple=False
|
103 |
+
),
|
104 |
+
html.Div(id='pink-review-file-name')
|
105 |
+
]),
|
106 |
dcc.Loading(
|
107 |
id="chat-loading",
|
108 |
type="dot",
|
|
|
137 |
|
138 |
@app.callback(
|
139 |
Output('file-list', 'children'),
|
140 |
+
Output('status-bar', 'children'),
|
141 |
Input('upload-document', 'contents'),
|
142 |
State('upload-document', 'filename'),
|
143 |
State('file-list', 'children')
|
144 |
)
|
145 |
def update_output(list_of_contents, list_of_names, existing_files):
|
146 |
+
global uploaded_files, shredded_document
|
147 |
if list_of_contents is not None:
|
148 |
new_files = []
|
149 |
for i, (content, name) in enumerate(zip(list_of_contents, list_of_names)):
|
|
|
155 |
]))
|
156 |
if existing_files is None:
|
157 |
existing_files = []
|
158 |
+
shredded_document = None # Reset shredded document when new files are uploaded
|
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 |
|
162 |
@app.callback(
|
163 |
Output('file-list', 'children', allow_duplicate=True),
|
164 |
+
Output('status-bar', 'children', allow_duplicate=True),
|
165 |
Input({'type': 'remove-file', 'index': dash.ALL}, 'n_clicks'),
|
166 |
State('file-list', 'children'),
|
167 |
prevent_initial_call=True
|
168 |
)
|
169 |
def remove_file(n_clicks, existing_files):
|
170 |
+
global uploaded_files, shredded_document
|
171 |
ctx = dash.callback_context
|
172 |
if not ctx.triggered:
|
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 # Reset shredded document when a file is removed
|
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):
|
180 |
prompt = f"""Generate a {document_type} based on the following project artifacts:
|
|
|
193 |
@app.callback(
|
194 |
Output('document-preview', 'children'),
|
195 |
Output('loading-output', 'children'),
|
196 |
+
Output('status-bar', 'children', allow_duplicate=True),
|
197 |
+
Output('pink-review-upload', 'style'),
|
198 |
[Input(f'btn-{doc_type.lower().replace("_", "-")}', 'n_clicks') for doc_type in document_types.keys()],
|
199 |
+
State('pink-review-file-name', 'children'),
|
200 |
prevent_initial_call=True
|
201 |
)
|
202 |
def generate_document_preview(*args):
|
203 |
+
global current_document, document_type, shredded_document, pink_review_document
|
204 |
ctx = dash.callback_context
|
205 |
if not ctx.triggered:
|
206 |
raise dash.exceptions.PreventUpdate
|
207 |
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
|
208 |
document_type = button_id.replace('btn-', '').replace('-', '_').title()
|
209 |
+
pink_review_file = args[-1]
|
210 |
+
|
211 |
+
if not uploaded_files and document_type != "Shred":
|
212 |
+
return html.Div("Please upload and shred a document first."), "", "Please upload and shred a document first.", {'display': 'none'}
|
213 |
+
|
214 |
+
if document_type == "Shred":
|
215 |
+
if not uploaded_files:
|
216 |
+
return html.Div("Please upload a document before shredding."), "", "Please upload a document before shredding.", {'display': 'none'}
|
217 |
+
file_contents = list(uploaded_files.values())
|
218 |
+
try:
|
219 |
+
shredded_document = generate_document(document_type, file_contents)
|
220 |
+
return dcc.Markdown(shredded_document), f"{document_type} generated", "Document shredded. You can now proceed with other operations.", {'display': 'none'}
|
221 |
+
except Exception as e:
|
222 |
+
print(f"Error generating document: {str(e)}")
|
223 |
+
return html.Div(f"Error generating document: {str(e)}"), "Error", "An error occurred while shredding the document.", {'display': 'none'}
|
224 |
+
|
225 |
+
if shredded_document is None:
|
226 |
+
return html.Div("Please shred a document first."), "", "Please shred a document first.", {'display': 'none'}
|
227 |
+
|
228 |
+
if document_type == "Pink Review":
|
229 |
+
return html.Div("Please upload a Pink Team document or use the generated one."), "", "Please upload a Pink Team document or use the generated one.", {'display': 'block'}
|
230 |
+
|
231 |
+
if document_type in ["Red", "Red Review"] and pink_review_document is None:
|
232 |
+
return html.Div("Please complete Pink Review first."), "", "Please complete Pink Review first.", {'display': 'none'}
|
233 |
|
|
|
|
|
234 |
try:
|
235 |
+
if document_type == "Pink Review" and pink_review_file:
|
236 |
+
current_document = generate_document(document_type, [pink_review_file, shredded_document])
|
237 |
+
elif document_type in ["Red", "Red Review"]:
|
238 |
+
current_document = generate_document(document_type, [pink_review_document, shredded_document])
|
239 |
+
else:
|
240 |
+
current_document = generate_document(document_type, [shredded_document])
|
241 |
+
|
242 |
+
if document_type == "Pink Review":
|
243 |
+
pink_review_document = current_document
|
244 |
+
|
245 |
+
return dcc.Markdown(current_document), f"{document_type} generated", f"{document_type} document generated successfully.", {'display': 'none'}
|
246 |
except Exception as e:
|
247 |
print(f"Error generating document: {str(e)}")
|
248 |
+
return html.Div(f"Error generating document: {str(e)}"), "Error", "An error occurred while generating the document.", {'display': 'none'}
|
249 |
+
|
250 |
+
@app.callback(
|
251 |
+
Output('pink-review-file-name', 'children'),
|
252 |
+
Input('upload-pink-review', 'contents'),
|
253 |
+
State('upload-pink-review', 'filename')
|
254 |
+
)
|
255 |
+
def update_pink_review_filename(contents, filename):
|
256 |
+
if contents is not None:
|
257 |
+
return filename
|
258 |
+
return ""
|
259 |
|
260 |
@app.callback(
|
261 |
Output('chat-output', 'children'),
|
|
|
294 |
if current_document is None:
|
295 |
raise dash.exceptions.PreventUpdate
|
296 |
|
297 |
+
if document_type == "LOE":
|
298 |
+
# Create a pandas DataFrame for LOE
|
299 |
+
df = pd.read_csv(StringIO(current_document))
|
300 |
+
|
301 |
+
# Save the DataFrame to an Excel file
|
302 |
+
output = BytesIO()
|
303 |
+
with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
|
304 |
+
df.to_excel(writer, sheet_name='LOE', index=False)
|
305 |
+
|
306 |
+
return dcc.send_bytes(output.getvalue(), f"{document_type}.xlsx")
|
307 |
+
else:
|
308 |
+
# Create an in-memory Word document
|
309 |
+
doc = Document()
|
310 |
+
doc.add_paragraph(current_document)
|
311 |
+
|
312 |
+
# Save the document to a BytesIO object
|
313 |
+
output = BytesIO()
|
314 |
+
doc.save(output)
|
315 |
+
|
316 |
+
return dcc.send_bytes(output.getvalue(), f"{document_type}.docx")
|
317 |
|
318 |
if __name__ == '__main__':
|
319 |
print("Starting the Dash application...")
|