Update app.py
Browse files
app.py
CHANGED
|
@@ -11,8 +11,6 @@ from dash.exceptions import PreventUpdate
|
|
| 11 |
import PyPDF2
|
| 12 |
import docx
|
| 13 |
import chardet
|
| 14 |
-
import json
|
| 15 |
-
from dash.exceptions import PreventUpdate
|
| 16 |
|
| 17 |
# Initialize the Dash app
|
| 18 |
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
|
|
@@ -68,48 +66,30 @@ app.layout = dbc.Container([
|
|
| 68 |
},
|
| 69 |
multiple=True
|
| 70 |
),
|
| 71 |
-
html.Div(
|
| 72 |
-
html.H5("Uploaded Files"),
|
| 73 |
-
dbc.ListGroup(id='file-list')
|
| 74 |
-
], className="mt-3"),
|
| 75 |
html.Hr(),
|
| 76 |
html.Div([
|
| 77 |
dbc.Button(
|
| 78 |
matrix_type,
|
| 79 |
-
id={
|
| 80 |
-
color="
|
| 81 |
-
className="mb-2 w-100
|
| 82 |
-
style={
|
| 83 |
-
'textAlign': 'left',
|
| 84 |
-
'textDecoration': 'none',
|
| 85 |
-
'padding': '0.375rem 0.75rem',
|
| 86 |
-
'transition': 'background-color 0.3s'
|
| 87 |
-
}
|
| 88 |
) for matrix_type in matrix_types.keys()
|
| 89 |
-
]
|
| 90 |
], width=3),
|
| 91 |
dbc.Col([
|
| 92 |
-
html.Div(style={"height": "20px"}), #
|
| 93 |
-
html.Div(id="loading-output"), # Text status box
|
| 94 |
-
html.Div([ # New row for progress indicator
|
| 95 |
-
dcc.Loading(
|
| 96 |
-
id="loading-indicator",
|
| 97 |
-
type="dot",
|
| 98 |
-
children=[html.Div(style={"height": "50px"})] # Increased height for visibility
|
| 99 |
-
),
|
| 100 |
-
], className="my-3"), # Add margin for separation
|
| 101 |
-
html.Div(id='matrix-preview', className="border p-3 mb-3"),
|
| 102 |
-
html.Div(id="loading-output"), # Text status box
|
| 103 |
dcc.Loading(
|
| 104 |
id="loading-indicator",
|
| 105 |
type="dot",
|
| 106 |
-
children=[html.Div()]
|
| 107 |
),
|
| 108 |
html.Div(id='matrix-preview', className="border p-3 mb-3"),
|
| 109 |
dbc.Button("Download Matrix", id="btn-download", color="success", className="mt-3"),
|
| 110 |
dcc.Download(id="download-matrix"),
|
| 111 |
html.Hr(),
|
| 112 |
-
html.Div(style={"height": "20px"}), #
|
| 113 |
dcc.Loading(
|
| 114 |
id="chat-loading",
|
| 115 |
type="dot",
|
|
@@ -120,10 +100,7 @@ app.layout = dbc.Container([
|
|
| 120 |
]
|
| 121 |
)
|
| 122 |
], width=9)
|
| 123 |
-
])
|
| 124 |
-
html.Div(id='uploaded-files-state', style={'display': 'none'}),
|
| 125 |
-
dcc.Store(id='matrix-type-store'),
|
| 126 |
-
html.Div(id='matrix-button-clicks', style={'display': 'none'})
|
| 127 |
], fluid=True)
|
| 128 |
|
| 129 |
def parse_file_content(contents, filename):
|
|
@@ -157,17 +134,17 @@ def update_output(list_of_contents, list_of_names, existing_files):
|
|
| 157 |
global uploaded_files
|
| 158 |
if list_of_contents is not None:
|
| 159 |
new_files = []
|
| 160 |
-
for content, name in zip(list_of_contents, list_of_names):
|
| 161 |
file_content = parse_file_content(content, name)
|
| 162 |
uploaded_files[name] = file_content
|
| 163 |
-
new_files.append(
|
| 164 |
-
|
| 165 |
-
color="danger", size="sm", className="me-2"),
|
| 166 |
html.Span(name)
|
| 167 |
]))
|
| 168 |
-
|
|
|
|
| 169 |
return existing_files + new_files
|
| 170 |
-
return existing_files
|
| 171 |
|
| 172 |
@app.callback(
|
| 173 |
Output('file-list', 'children', allow_duplicate=True),
|
|
@@ -180,25 +157,10 @@ def remove_file(n_clicks, existing_files):
|
|
| 180 |
ctx = dash.callback_context
|
| 181 |
if not ctx.triggered:
|
| 182 |
raise PreventUpdate
|
| 183 |
-
|
| 184 |
-
triggered_id = ctx.triggered[0]['prop_id']
|
| 185 |
-
removed_file = triggered_id.split('"index":')[1].split('}')[0].strip('"')
|
| 186 |
-
|
| 187 |
uploaded_files.pop(removed_file, None)
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
return [
|
| 191 |
-
file for file in existing_files
|
| 192 |
-
if isinstance(file, dict) and
|
| 193 |
-
'props' in file and
|
| 194 |
-
'children' in file['props'] and
|
| 195 |
-
isinstance(file['props']['children'], list) and
|
| 196 |
-
len(file['props']['children']) > 1 and
|
| 197 |
-
isinstance(file['props']['children'][1], dict) and
|
| 198 |
-
'children' in file['props']['children'][1] and
|
| 199 |
-
file['props']['children'][1]['children'] != removed_file
|
| 200 |
-
]
|
| 201 |
-
|
| 202 |
def generate_matrix_with_gpt(matrix_type, file_contents):
|
| 203 |
prompt = f"""Generate a {matrix_type} based on the following project artifacts:
|
| 204 |
{' '.join(file_contents)}
|
|
@@ -237,40 +199,27 @@ Now, generate the {matrix_type}:
|
|
| 237 |
|
| 238 |
return pd.DataFrame(data, columns=headers)
|
| 239 |
|
| 240 |
-
@app.callback(
|
| 241 |
-
Output('uploaded-files-state', 'children'),
|
| 242 |
-
Input('file-list', 'children')
|
| 243 |
-
)
|
| 244 |
-
def update_uploaded_files_state(file_list):
|
| 245 |
-
global uploaded_files
|
| 246 |
-
return json.dumps(list(uploaded_files.keys()))
|
| 247 |
-
|
| 248 |
@app.callback(
|
| 249 |
Output('matrix-preview', 'children'),
|
| 250 |
Output('loading-output', 'children'),
|
| 251 |
-
Input({
|
| 252 |
-
State('uploaded-files-state', 'children'),
|
| 253 |
prevent_initial_call=True
|
| 254 |
)
|
| 255 |
-
def generate_matrix(
|
| 256 |
global current_matrix, matrix_type
|
| 257 |
ctx = dash.callback_context
|
| 258 |
if not ctx.triggered:
|
| 259 |
raise PreventUpdate
|
|
|
|
|
|
|
| 260 |
|
| 261 |
-
|
| 262 |
-
matrix_type = json.loads(button_id.split('.')[0])['index']
|
| 263 |
-
|
| 264 |
-
if not uploaded_files_json:
|
| 265 |
return html.Div("Please upload project artifacts before generating a matrix."), ""
|
| 266 |
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
if not uploaded_files_list:
|
| 270 |
-
return html.Div("Please upload project artifacts before generating a matrix."), ""
|
| 271 |
|
| 272 |
try:
|
| 273 |
-
current_matrix = generate_matrix_with_gpt(matrix_type,
|
| 274 |
return dbc.Table.from_dataframe(current_matrix, striped=True, bordered=True, hover=True), f"{matrix_type} generated"
|
| 275 |
except Exception as e:
|
| 276 |
print(f"Error generating matrix: {str(e)}")
|
|
|
|
| 11 |
import PyPDF2
|
| 12 |
import docx
|
| 13 |
import chardet
|
|
|
|
|
|
|
| 14 |
|
| 15 |
# Initialize the Dash app
|
| 16 |
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
|
|
|
|
| 66 |
},
|
| 67 |
multiple=True
|
| 68 |
),
|
| 69 |
+
html.Div(id='file-list'),
|
|
|
|
|
|
|
|
|
|
| 70 |
html.Hr(),
|
| 71 |
html.Div([
|
| 72 |
dbc.Button(
|
| 73 |
matrix_type,
|
| 74 |
+
id=f'btn-{matrix_type.lower().replace(" ", "-")}',
|
| 75 |
+
color="primary",
|
| 76 |
+
className="mb-2 w-100",
|
| 77 |
+
style={'overflow': 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap'}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
) for matrix_type in matrix_types.keys()
|
| 79 |
+
])
|
| 80 |
], width=3),
|
| 81 |
dbc.Col([
|
| 82 |
+
html.Div(style={"height": "20px"}), # Added small gap
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
dcc.Loading(
|
| 84 |
id="loading-indicator",
|
| 85 |
type="dot",
|
| 86 |
+
children=[html.Div(id="loading-output")]
|
| 87 |
),
|
| 88 |
html.Div(id='matrix-preview', className="border p-3 mb-3"),
|
| 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"}), # Added small gap
|
| 93 |
dcc.Loading(
|
| 94 |
id="chat-loading",
|
| 95 |
type="dot",
|
|
|
|
| 100 |
]
|
| 101 |
)
|
| 102 |
], width=9)
|
| 103 |
+
])
|
|
|
|
|
|
|
|
|
|
| 104 |
], fluid=True)
|
| 105 |
|
| 106 |
def parse_file_content(contents, filename):
|
|
|
|
| 134 |
global uploaded_files
|
| 135 |
if list_of_contents is not None:
|
| 136 |
new_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(html.Div([
|
| 141 |
+
html.Button('×', id={'type': 'remove-file', 'index': name}, style={'marginRight': '5px', 'fontSize': '10px'}),
|
|
|
|
| 142 |
html.Span(name)
|
| 143 |
]))
|
| 144 |
+
if existing_files is None:
|
| 145 |
+
existing_files = []
|
| 146 |
return existing_files + new_files
|
| 147 |
+
return existing_files
|
| 148 |
|
| 149 |
@app.callback(
|
| 150 |
Output('file-list', 'children', allow_duplicate=True),
|
|
|
|
| 157 |
ctx = dash.callback_context
|
| 158 |
if not ctx.triggered:
|
| 159 |
raise PreventUpdate
|
| 160 |
+
removed_file = ctx.triggered[0]['prop_id'].split(',')[0].split(':')[-1].strip('}')
|
|
|
|
|
|
|
|
|
|
| 161 |
uploaded_files.pop(removed_file, None)
|
| 162 |
+
return [file for file in existing_files if file['props']['children'][1]['props']['children'] != removed_file]
|
| 163 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
def generate_matrix_with_gpt(matrix_type, file_contents):
|
| 165 |
prompt = f"""Generate a {matrix_type} based on the following project artifacts:
|
| 166 |
{' '.join(file_contents)}
|
|
|
|
| 199 |
|
| 200 |
return pd.DataFrame(data, columns=headers)
|
| 201 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
@app.callback(
|
| 203 |
Output('matrix-preview', 'children'),
|
| 204 |
Output('loading-output', 'children'),
|
| 205 |
+
[Input(f'btn-{matrix_type.lower().replace(" ", "-")}', 'n_clicks') for matrix_type in matrix_types.keys()],
|
|
|
|
| 206 |
prevent_initial_call=True
|
| 207 |
)
|
| 208 |
+
def generate_matrix(*args):
|
| 209 |
global current_matrix, matrix_type
|
| 210 |
ctx = dash.callback_context
|
| 211 |
if not ctx.triggered:
|
| 212 |
raise PreventUpdate
|
| 213 |
+
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
|
| 214 |
+
matrix_type = button_id.replace('btn-', '').replace('-', ' ').title()
|
| 215 |
|
| 216 |
+
if not uploaded_files:
|
|
|
|
|
|
|
|
|
|
| 217 |
return html.Div("Please upload project artifacts before generating a matrix."), ""
|
| 218 |
|
| 219 |
+
file_contents = list(uploaded_files.values())
|
|
|
|
|
|
|
|
|
|
| 220 |
|
| 221 |
try:
|
| 222 |
+
current_matrix = generate_matrix_with_gpt(matrix_type, file_contents)
|
| 223 |
return dbc.Table.from_dataframe(current_matrix, striped=True, bordered=True, hover=True), f"{matrix_type} generated"
|
| 224 |
except Exception as e:
|
| 225 |
print(f"Error generating matrix: {str(e)}")
|