Update app.py via AI Editor
Browse files
app.py
CHANGED
|
@@ -12,18 +12,14 @@ import PyPDF2
|
|
| 12 |
import docx
|
| 13 |
import chardet
|
| 14 |
|
| 15 |
-
# Initialize the Dash app
|
| 16 |
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
|
| 17 |
|
| 18 |
-
# Get OpenAI API key from Hugging Face Spaces environment variable
|
| 19 |
openai.api_key = os.environ.get('OPENAI_API_KEY')
|
| 20 |
|
| 21 |
-
# Global variables
|
| 22 |
uploaded_files = {}
|
| 23 |
current_matrix = None
|
| 24 |
matrix_type = None
|
| 25 |
|
| 26 |
-
# Matrix types and their descriptions
|
| 27 |
matrix_types = {
|
| 28 |
"Project Deliverables Matrix": "Generate a project deliverables matrix all presumed and actual deliverables based on tasks, requirements and scope.",
|
| 29 |
"Communications Plan Matrix": "Create a matrix showing stakeholders, communication methods, frequency, and responsibilities.",
|
|
@@ -79,7 +75,7 @@ app.layout = dbc.Container([
|
|
| 79 |
])
|
| 80 |
], width=3),
|
| 81 |
dbc.Col([
|
| 82 |
-
html.Div(style={"height": "20px"}),
|
| 83 |
dcc.Loading(
|
| 84 |
id="loading-indicator",
|
| 85 |
type="dot",
|
|
@@ -89,7 +85,7 @@ app.layout = dbc.Container([
|
|
| 89 |
dbc.Button("Download Matrix", id="btn-download", color="success", className="mt-3"),
|
| 90 |
dcc.Download(id="download-matrix"),
|
| 91 |
html.Hr(),
|
| 92 |
-
html.Div(style={"height": "20px"}),
|
| 93 |
dcc.Loading(
|
| 94 |
id="chat-loading",
|
| 95 |
type="dot",
|
|
@@ -124,6 +120,12 @@ def parse_file_content(contents, filename):
|
|
| 124 |
print(f"Error processing file {filename}: {str(e)}")
|
| 125 |
return "Error processing file"
|
| 126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
@app.callback(
|
| 128 |
Output('file-list', 'children'),
|
| 129 |
Input('upload-files', 'contents'),
|
|
@@ -137,10 +139,47 @@ def update_output(list_of_contents, list_of_names, existing_files):
|
|
| 137 |
for i, (content, name) in enumerate(zip(list_of_contents, list_of_names)):
|
| 138 |
file_content = parse_file_content(content, name)
|
| 139 |
uploaded_files[name] = file_content
|
| 140 |
-
new_files.append(
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
if existing_files is None:
|
| 145 |
existing_files = []
|
| 146 |
return existing_files + new_files
|
|
@@ -157,9 +196,32 @@ def remove_file(n_clicks, existing_files):
|
|
| 157 |
ctx = dash.callback_context
|
| 158 |
if not ctx.triggered:
|
| 159 |
raise PreventUpdate
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
uploaded_files.pop(removed_file, None)
|
| 162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
def generate_matrix_with_gpt(matrix_type, file_contents):
|
| 165 |
prompt = f"""Generate a {matrix_type} based on the following project artifacts:
|
|
@@ -188,7 +250,7 @@ Now, generate the {matrix_type}:
|
|
| 188 |
)
|
| 189 |
|
| 190 |
matrix_text = response.choices[0].message.content.strip()
|
| 191 |
-
print("Raw matrix text from GPT:", matrix_text)
|
| 192 |
|
| 193 |
lines = [line.strip() for line in matrix_text.split('\n') if '|' in line]
|
| 194 |
data = [line.split('|') for line in lines]
|
|
@@ -260,7 +322,7 @@ Now, provide the updated {matrix_type}:
|
|
| 260 |
)
|
| 261 |
|
| 262 |
updated_matrix_text = response.choices[0].message.content.strip()
|
| 263 |
-
print("Raw updated matrix text from GPT:", updated_matrix_text)
|
| 264 |
|
| 265 |
lines = [line.strip() for line in updated_matrix_text.split('\n') if '|' in line]
|
| 266 |
data = [line.split('|') for line in lines]
|
|
@@ -283,7 +345,6 @@ def download_matrix(n_clicks):
|
|
| 283 |
if current_matrix is None:
|
| 284 |
raise PreventUpdate
|
| 285 |
|
| 286 |
-
# Create an in-memory Excel file
|
| 287 |
output = io.BytesIO()
|
| 288 |
with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
|
| 289 |
current_matrix.to_excel(writer, sheet_name='Sheet1', index=False)
|
|
|
|
| 12 |
import docx
|
| 13 |
import chardet
|
| 14 |
|
|
|
|
| 15 |
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
|
| 16 |
|
|
|
|
| 17 |
openai.api_key = os.environ.get('OPENAI_API_KEY')
|
| 18 |
|
|
|
|
| 19 |
uploaded_files = {}
|
| 20 |
current_matrix = None
|
| 21 |
matrix_type = None
|
| 22 |
|
|
|
|
| 23 |
matrix_types = {
|
| 24 |
"Project Deliverables Matrix": "Generate a project deliverables matrix all presumed and actual deliverables based on tasks, requirements and scope.",
|
| 25 |
"Communications Plan Matrix": "Create a matrix showing stakeholders, communication methods, frequency, and responsibilities.",
|
|
|
|
| 75 |
])
|
| 76 |
], width=3),
|
| 77 |
dbc.Col([
|
| 78 |
+
html.Div(style={"height": "20px"}),
|
| 79 |
dcc.Loading(
|
| 80 |
id="loading-indicator",
|
| 81 |
type="dot",
|
|
|
|
| 85 |
dbc.Button("Download Matrix", id="btn-download", color="success", className="mt-3"),
|
| 86 |
dcc.Download(id="download-matrix"),
|
| 87 |
html.Hr(),
|
| 88 |
+
html.Div(style={"height": "20px"}),
|
| 89 |
dcc.Loading(
|
| 90 |
id="chat-loading",
|
| 91 |
type="dot",
|
|
|
|
| 120 |
print(f"Error processing file {filename}: {str(e)}")
|
| 121 |
return "Error processing file"
|
| 122 |
|
| 123 |
+
def truncate_filename(filename, max_length=24):
|
| 124 |
+
if len(filename) <= max_length:
|
| 125 |
+
return filename
|
| 126 |
+
else:
|
| 127 |
+
return filename[:max_length - 3] + '...'
|
| 128 |
+
|
| 129 |
@app.callback(
|
| 130 |
Output('file-list', 'children'),
|
| 131 |
Input('upload-files', 'contents'),
|
|
|
|
| 139 |
for i, (content, name) in enumerate(zip(list_of_contents, list_of_names)):
|
| 140 |
file_content = parse_file_content(content, name)
|
| 141 |
uploaded_files[name] = file_content
|
| 142 |
+
new_files.append(
|
| 143 |
+
dbc.Card(
|
| 144 |
+
dbc.CardBody(
|
| 145 |
+
dbc.Row([
|
| 146 |
+
dbc.Col(
|
| 147 |
+
html.Span(
|
| 148 |
+
truncate_filename(name),
|
| 149 |
+
title=name,
|
| 150 |
+
style={
|
| 151 |
+
'display': 'inline-block',
|
| 152 |
+
'overflow': 'hidden',
|
| 153 |
+
'textOverflow': 'ellipsis',
|
| 154 |
+
'whiteSpace': 'nowrap',
|
| 155 |
+
'maxWidth': '90%',
|
| 156 |
+
'verticalAlign': 'middle',
|
| 157 |
+
}
|
| 158 |
+
),
|
| 159 |
+
width='auto',
|
| 160 |
+
style={'display': 'flex', 'alignItems': 'center', 'padding': '0'}
|
| 161 |
+
),
|
| 162 |
+
dbc.Col(
|
| 163 |
+
dbc.Button(
|
| 164 |
+
"Delete",
|
| 165 |
+
id={'type': 'remove-file', 'index': name},
|
| 166 |
+
color="danger",
|
| 167 |
+
size="sm",
|
| 168 |
+
style={'marginLeft': 'auto', 'float': 'right'}
|
| 169 |
+
),
|
| 170 |
+
width='auto',
|
| 171 |
+
style={'display': 'flex', 'alignItems': 'center', 'justifyContent': 'flex-end', 'padding': '0'}
|
| 172 |
+
),
|
| 173 |
+
],
|
| 174 |
+
justify="between",
|
| 175 |
+
align="center",
|
| 176 |
+
style={"margin": "0", "padding": "0"}
|
| 177 |
+
),
|
| 178 |
+
style={'padding': '6px 8px', 'margin': '0', 'display': 'flex', 'alignItems': 'center', 'background': 'none', 'boxShadow': 'none'}
|
| 179 |
+
),
|
| 180 |
+
style={'border': 'none', 'boxShadow': 'none', 'background': 'none', 'marginBottom': '2px'}
|
| 181 |
+
)
|
| 182 |
+
)
|
| 183 |
if existing_files is None:
|
| 184 |
existing_files = []
|
| 185 |
return existing_files + new_files
|
|
|
|
| 196 |
ctx = dash.callback_context
|
| 197 |
if not ctx.triggered:
|
| 198 |
raise PreventUpdate
|
| 199 |
+
# Find which button was pressed
|
| 200 |
+
triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
|
| 201 |
+
# triggered_id is a dict-like string, e.g. "{'type':'remove-file','index':'filename'}"
|
| 202 |
+
# Safely eval to dict (since dash handles this)
|
| 203 |
+
import ast
|
| 204 |
+
try:
|
| 205 |
+
triggered_id_dict = ast.literal_eval(triggered_id)
|
| 206 |
+
removed_file = triggered_id_dict['index']
|
| 207 |
+
except Exception:
|
| 208 |
+
raise PreventUpdate
|
| 209 |
uploaded_files.pop(removed_file, None)
|
| 210 |
+
# Filter out the file that was removed
|
| 211 |
+
filtered_files = []
|
| 212 |
+
for file_card in existing_files:
|
| 213 |
+
# file_card is a dict representing a dbc.Card
|
| 214 |
+
# Traverse to get filename from card body
|
| 215 |
+
try:
|
| 216 |
+
# Card > CardBody > Row > [Col, Col] > Col[0] > Span
|
| 217 |
+
filename_span = file_card['props']['children']['props']['children'][0]['props']['children'][0]['props']['children']
|
| 218 |
+
# The span text is truncated; check its title for the full name
|
| 219 |
+
span_title = file_card['props']['children']['props']['children'][0]['props']['children'][0]['props']['title']
|
| 220 |
+
if span_title != removed_file:
|
| 221 |
+
filtered_files.append(file_card)
|
| 222 |
+
except Exception:
|
| 223 |
+
filtered_files.append(file_card)
|
| 224 |
+
return filtered_files
|
| 225 |
|
| 226 |
def generate_matrix_with_gpt(matrix_type, file_contents):
|
| 227 |
prompt = f"""Generate a {matrix_type} based on the following project artifacts:
|
|
|
|
| 250 |
)
|
| 251 |
|
| 252 |
matrix_text = response.choices[0].message.content.strip()
|
| 253 |
+
print("Raw matrix text from GPT:", matrix_text)
|
| 254 |
|
| 255 |
lines = [line.strip() for line in matrix_text.split('\n') if '|' in line]
|
| 256 |
data = [line.split('|') for line in lines]
|
|
|
|
| 322 |
)
|
| 323 |
|
| 324 |
updated_matrix_text = response.choices[0].message.content.strip()
|
| 325 |
+
print("Raw updated matrix text from GPT:", updated_matrix_text)
|
| 326 |
|
| 327 |
lines = [line.strip() for line in updated_matrix_text.split('\n') if '|' in line]
|
| 328 |
data = [line.split('|') for line in lines]
|
|
|
|
| 345 |
if current_matrix is None:
|
| 346 |
raise PreventUpdate
|
| 347 |
|
|
|
|
| 348 |
output = io.BytesIO()
|
| 349 |
with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
|
| 350 |
current_matrix.to_excel(writer, sheet_name='Sheet1', index=False)
|