sushintern01 commited on
Commit
e2a80d5
·
verified ·
1 Parent(s): ac4deb2

Upload 9 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ static/uploads/benchmark.jpg filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify, url_for, session, send_file, Response
2
+ import torch
3
+ from PIL import Image
4
+ import pandas as pd
5
+ import re
6
+ import os
7
+ import base64
8
+ import json
9
+ import traceback
10
+ from io import BytesIO
11
+ from transformers import AutoProcessor, PaliGemmaForConditionalGeneration
12
+
13
+ app = Flask(__name__)
14
+ app.secret_key = os.urandom(24) # Required for session
15
+ UPLOAD_FOLDER = 'static/uploads/'
16
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
17
+ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'} # Add allowed extensions
18
+
19
+ if not os.path.exists(UPLOAD_FOLDER):
20
+ os.makedirs(UPLOAD_FOLDER)
21
+
22
+ # Load PaliGemma model and processor (load once)
23
+ def load_paligemma_model():
24
+ try:
25
+ print("Loading PaliGemma model from local path...")
26
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
27
+ print(f"Using device: {device}")
28
+
29
+ # Specify the local path relative to your project structure
30
+ local_model_path = os.path.join(os.path.dirname(__file__), 'Model') # Update this path
31
+
32
+ # Load model and processor from the specified local path
33
+ model = PaliGemmaForConditionalGeneration.from_pretrained(
34
+ local_model_path,
35
+ torch_dtype=torch.float16
36
+ )
37
+ processor = AutoProcessor.from_pretrained(local_model_path)
38
+ model = model.to(device)
39
+ print("Model loaded successfully")
40
+ return model, processor, device
41
+ except Exception as e:
42
+ print(f"Error loading model: {str(e)}")
43
+ traceback.print_exc()
44
+ raise
45
+
46
+
47
+ # Store the model in the app context
48
+ with app.app_context():
49
+ app.paligemma_model, app.paligemma_processor, app.device = load_paligemma_model()
50
+
51
+ # Helper function to check allowed extensions
52
+ def allowed_file(filename):
53
+ return '.' in filename and \
54
+ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
55
+
56
+ # Clean model output function - improved like the Streamlit version
57
+ def clean_model_output(text):
58
+ if not text:
59
+ print("Warning: Empty text passed to clean_model_output")
60
+ return ""
61
+
62
+ # Check if the entire response is a print statement and extract its content
63
+ print_match = re.search(r'^print\(["\'](.+?)["\']\)$', text.strip())
64
+ if print_match:
65
+ return print_match.group(1)
66
+
67
+ # Remove all print statements
68
+ text = re.sub(r'print\(.+?\)', '', text, flags=re.DOTALL)
69
+
70
+ # Remove Python code formatting artifacts
71
+ text = re.sub(r'```python|```', '', text)
72
+
73
+ return text.strip()
74
+
75
+ # Analyze chart function
76
+ def analyze_chart_with_paligemma(image, query, use_cot=False):
77
+ try:
78
+ print(f"Starting analysis with query: {query}")
79
+ print(f"Use CoT: {use_cot}")
80
+
81
+ model = app.paligemma_model
82
+ processor = app.paligemma_processor
83
+ device = app.device
84
+
85
+ # Add program of thought prefix if CoT is enabled (matching Streamlit version)
86
+ if use_cot and not query.startswith("program of thought:"):
87
+ modified_query = f"program of thought: {query}"
88
+ else:
89
+ modified_query = query
90
+
91
+ print(f"Modified query: {modified_query}")
92
+
93
+ # Process inputs
94
+ try:
95
+ print("Processing inputs...")
96
+ inputs = processor(text=modified_query, images=image, return_tensors="pt")
97
+ print(f"Input keys: {inputs.keys()}")
98
+ prompt_length = inputs['input_ids'].shape[1] # Store prompt length for later use
99
+ inputs = {k: v.to(device) for k, v in inputs.items()}
100
+ except Exception as e:
101
+ print(f"Error processing inputs: {str(e)}")
102
+ traceback.print_exc()
103
+ return f"Error processing inputs: {str(e)}"
104
+
105
+ # Generate output
106
+ try:
107
+ print("Generating output...")
108
+ with torch.no_grad():
109
+ generate_ids = model.generate(
110
+ **inputs,
111
+ num_beams=4,
112
+ max_new_tokens=512,
113
+ output_scores=True,
114
+ return_dict_in_generate=True
115
+ )
116
+
117
+ output_text = processor.batch_decode(
118
+ generate_ids.sequences[:, prompt_length:],
119
+ skip_special_tokens=True,
120
+ clean_up_tokenization_spaces=False
121
+ )[0]
122
+
123
+ print(f"Raw output text: {output_text}")
124
+ cleaned_output = clean_model_output(output_text)
125
+ print(f"Cleaned output text: {cleaned_output}")
126
+ return cleaned_output
127
+ except Exception as e:
128
+ print(f"Error generating output: {str(e)}")
129
+ traceback.print_exc()
130
+ return f"Error generating output: {str(e)}"
131
+
132
+ except Exception as e:
133
+ print(f"Error in analyze_chart_with_paligemma: {str(e)}")
134
+ traceback.print_exc()
135
+ return f"Error: {str(e)}"
136
+
137
+ # Extract data points function - updated to match Streamlit version
138
+ def extract_data_points(image):
139
+ print("Starting data extraction...")
140
+ try:
141
+ # Special query to extract data points - same as Streamlit
142
+ extraction_query = "program of thought: Extract all data points from this chart. List each category or series and all its corresponding values in a structured format."
143
+
144
+ print(f"Using extraction query: {extraction_query}")
145
+ result = analyze_chart_with_paligemma(image, extraction_query, use_cot=True)
146
+ print(f"Extraction result: {result}")
147
+
148
+ # Parse the result into a DataFrame using the improved parser
149
+ df = parse_chart_data(result)
150
+ return df
151
+ except Exception as e:
152
+ print(f"Error extracting data points: {str(e)}")
153
+ traceback.print_exc()
154
+ return pd.DataFrame({'Error': [str(e)]})
155
+
156
+ # Parse chart data function - completely revamped to match Streamlit's implementation
157
+ def parse_chart_data(text):
158
+ try:
159
+ # Clean the text from print statements first
160
+ text = clean_model_output(text)
161
+ print(f"Parsing cleaned text: {text}")
162
+
163
+ data = {}
164
+ lines = text.split('\n')
165
+ current_category = None
166
+
167
+ # First pass: Look for category and value pairs
168
+ for line in lines:
169
+ if not line.strip():
170
+ continue
171
+
172
+ if ':' in line and not re.search(r'\d+\.\d+', line):
173
+ current_category = line.split(':')[0].strip()
174
+ data[current_category] = []
175
+ elif current_category and (re.search(r'\d+', line) or ',' in line):
176
+ value_match = re.findall(r'[-+]?\d*\.\d+|\d+', line)
177
+ if value_match:
178
+ data[current_category].extend(value_match)
179
+
180
+ # Second pass: If no categories found, try alternative pattern matching
181
+ if not data:
182
+ table_pattern = r'(\w+(?:\s\w+)*)\s*[:|]\s*((?:\d+(?:\.\d+)?(?:\s*,\s*\d+(?:\.\d+)?)*)|(?:\d+(?:\.\d+)?))'
183
+ matches = re.findall(table_pattern, text)
184
+ for category, values in matches:
185
+ category = category.strip()
186
+ if category not in data:
187
+ data[category] = []
188
+ if ',' in values:
189
+ values = [v.strip() for v in values.split(',')]
190
+ else:
191
+ values = [values.strip()]
192
+ data[category].extend(values)
193
+
194
+ # Convert all values to float where possible
195
+ for key in data:
196
+ data[key] = [float(val) if re.match(r'^[-+]?\d*\.?\d+$', val) else val for val in data[key]]
197
+
198
+ # Create DataFrame
199
+ if data:
200
+ df = pd.DataFrame(data)
201
+ print(f"Successfully parsed data: {df.head()}")
202
+ else:
203
+ df = pd.DataFrame({'Extracted_Text': [text]})
204
+ print("Could not extract structured data, returning raw text")
205
+
206
+ return df
207
+ except Exception as e:
208
+ print(f"Error parsing chart data: {str(e)}")
209
+ traceback.print_exc()
210
+ return pd.DataFrame({'Raw_Text': [text]})
211
+
212
+ @app.route('/')
213
+ def index():
214
+ image_url = session.get('image_url', None)
215
+ return render_template('index.html', image_url=image_url)
216
+
217
+ @app.route('/upload', methods=['POST'])
218
+ def upload_image():
219
+ try:
220
+ if 'image' not in request.files:
221
+ return jsonify({"error": "No file uploaded"}), 400
222
+
223
+ file = request.files['image']
224
+ if file.filename == '':
225
+ return jsonify({"error": "No selected file"}), 400
226
+
227
+ if not allowed_file(file.filename):
228
+ return jsonify({"error": "Invalid file type"}), 400
229
+
230
+ filename = file.filename
231
+ file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
232
+ file.save(file_path)
233
+
234
+ session['image_url'] = url_for('static', filename=f'uploads/{filename}')
235
+ session['image_filename'] = filename
236
+ print(f"Image uploaded: {filename}")
237
+
238
+ return jsonify({"image_url": session['image_url']})
239
+
240
+ except Exception as e:
241
+ print(f"Error in upload_image: {str(e)}")
242
+ traceback.print_exc()
243
+ return jsonify({"error": str(e)}), 500
244
+
245
+ @app.route('/analyze', methods=['POST'])
246
+ def analyze_chart():
247
+ try:
248
+ query = request.form['query']
249
+ use_cot = request.form.get('use_cot') == 'true'
250
+ image_filename = session.get('image_filename')
251
+
252
+ if not image_filename:
253
+ return jsonify({"error": "No image found in session. Please upload an image first."}), 400
254
+
255
+ image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename)
256
+
257
+ if not os.path.exists(image_path):
258
+ return jsonify({"error": "Image not found. Please upload again."}), 400
259
+
260
+ image = Image.open(image_path).convert('RGB')
261
+ answer = analyze_chart_with_paligemma(image, query, use_cot)
262
+
263
+ return jsonify({"answer": answer})
264
+
265
+ except Exception as e:
266
+ print(f"Error in analyze_chart: {str(e)}")
267
+ traceback.print_exc()
268
+ return jsonify({"error": str(e)})
269
+
270
+ @app.route('/extract', methods=['POST'])
271
+ def extract_data():
272
+ try:
273
+ image_filename = session.get('image_filename')
274
+
275
+ if not image_filename:
276
+ return jsonify({"error": "No image found in session. Please upload an image first."}), 400
277
+
278
+ image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename)
279
+
280
+ if not os.path.exists(image_path):
281
+ return jsonify({"error": "Image not found. Please upload again."}), 400
282
+
283
+ image = Image.open(image_path).convert('RGB')
284
+ df = extract_data_points(image)
285
+
286
+ # Check if DataFrame is empty or contains only error messages
287
+ if df.empty:
288
+ return jsonify({"error": "Could not extract data from the image"}), 400
289
+
290
+ # Convert DataFrame to CSV data
291
+ csv_data = df.to_csv(index=False)
292
+ print(f"CSV data generated: {csv_data[:100]}...") # Print first 100 chars
293
+
294
+ # Encode CSV data to base64
295
+ csv_base64 = base64.b64encode(csv_data.encode()).decode('utf-8')
296
+
297
+ return jsonify({"csv_data": csv_base64})
298
+
299
+ except Exception as e:
300
+ print(f"Error in extract_data: {str(e)}")
301
+ traceback.print_exc()
302
+ return jsonify({"error": str(e)})
303
+
304
+ @app.route('/download_csv')
305
+ def download_csv():
306
+ try:
307
+ print("Download CSV route called")
308
+ image_filename = session.get('image_filename')
309
+
310
+ if not image_filename:
311
+ print("No image in session")
312
+ return jsonify({"error": "No image found in session. Please upload an image first."}), 400
313
+
314
+ image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename)
315
+ print(f"Looking for image at: {image_path}")
316
+
317
+ if not os.path.exists(image_path):
318
+ print("Image file not found")
319
+ return jsonify({"error": "Image not found. Please upload again."}), 400
320
+
321
+ print("Loading image")
322
+ image = Image.open(image_path).convert('RGB')
323
+ print("Extracting data points")
324
+ df = extract_data_points(image)
325
+
326
+ print(f"DataFrame: {df}")
327
+
328
+ # Create a BytesIO object to hold the CSV data in memory
329
+ csv_buffer = BytesIO()
330
+ df.to_csv(csv_buffer, index=False, encoding='utf-8')
331
+ csv_buffer.seek(0) # Reset the buffer's position to the beginning
332
+
333
+ # Debug: print CSV content
334
+ csv_content = csv_buffer.getvalue().decode('utf-8')
335
+ print(f"CSV Content: {csv_content}")
336
+ csv_buffer.seek(0) # Reset buffer position again after reading
337
+
338
+ print("Preparing response")
339
+ # Create direct response with CSV data
340
+ response = Response(
341
+ csv_buffer.getvalue(),
342
+ mimetype='text/csv',
343
+ headers={
344
+ 'Content-Disposition': 'attachment; filename=extracted_data.csv',
345
+ 'Content-Type': 'text/csv'
346
+ }
347
+ )
348
+
349
+ print("Returning CSV response")
350
+ return response
351
+
352
+ except Exception as e:
353
+ print(f"Error in download_csv: {str(e)}")
354
+ traceback.print_exc()
355
+ return jsonify({"error": str(e)}), 500
356
+
357
+ # Create a utility function to match the Streamlit version
358
+ def get_csv_download_link(df, filename="chart_data.csv"):
359
+ csv = df.to_csv(index=False)
360
+ b64 = base64.b64encode(csv.encode()).decode()
361
+ href = f'<a href="data:file/csv;base64,{b64}" download="{filename}">Download CSV File</a>'
362
+ return href
363
+
364
+ if __name__ == '__main__':
365
+ app.run(debug=True)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ flask
2
+ torch
3
+ transformers
4
+ pillow
5
+ requests
6
+ pandas
7
+ matplotlib
static/css/style.css ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: sans-serif;
3
+ margin: 20px;
4
+ }
5
+
6
+ h1 {
7
+ text-align: center;
8
+ }
9
+
10
+ #uploadSection,
11
+ #imageSection,
12
+ #analysisSection,
13
+ #extractSection,
14
+ #downloadSection {
15
+ margin-bottom: 20px;
16
+ padding: 10px;
17
+ border: 1px solid #ddd;
18
+ }
19
+
20
+ label {
21
+ display: block;
22
+ margin-bottom: 5px;
23
+ }
24
+
25
+ input[type="text"],
26
+ input[type="file"] {
27
+ width: 100%;
28
+ padding: 8px;
29
+ margin-bottom: 10px;
30
+ border: 1px solid #ccc;
31
+ box-sizing: border-box;
32
+ }
33
+
34
+ button {
35
+ background-color: #4CAF50;
36
+ color: white;
37
+ padding: 10px 15px;
38
+ border: none;
39
+ cursor: pointer;
40
+ }
41
+
42
+ button:hover {
43
+ background-color: #3e8e41;
44
+ }
45
+
46
+ #analysisResults {
47
+ margin-top: 10px;
48
+ padding: 10px;
49
+ border: 1px solid #ddd;
50
+ }
static/js/script.js ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ const uploadForm = document.getElementById('uploadForm');
3
+ const uploadStatus = document.getElementById('uploadStatus');
4
+ const imageSection = document.getElementById('imageSection');
5
+ const chartPreview = document.getElementById('chartPreview');
6
+ const analysisSection = document.getElementById('analysisSection');
7
+ const analysisResults = document.getElementById('analysisResults');
8
+ const analyzeButton = document.getElementById('analyzeButton');
9
+ const queryInput = document.getElementById('query');
10
+ const useCotCheckbox = document.getElementById('use_cot');
11
+ const extractSection = document.getElementById('extractSection');
12
+ const extractButton = document.getElementById('extractButton');
13
+ const downloadSection = document.getElementById('downloadSection');
14
+ const downloadLink = document.getElementById('downloadLink');
15
+ const extractStatus = document.getElementById('extractStatus');
16
+
17
+ // Function to show a message
18
+ function showMessage(element, message, isError = false) {
19
+ element.textContent = message;
20
+ element.style.color = isError ? 'red' : 'green';
21
+ }
22
+
23
+ // Function to clear a message
24
+ function clearMessage(element) {
25
+ element.textContent = '';
26
+ }
27
+
28
+ // Handle image upload
29
+ uploadForm.addEventListener('submit', async function(event) {
30
+ event.preventDefault();
31
+ clearMessage(uploadStatus);
32
+
33
+ const formData = new FormData(uploadForm);
34
+
35
+ try {
36
+ const response = await fetch('/upload', {
37
+ method: 'POST',
38
+ body: formData
39
+ });
40
+
41
+ const data = await response.json();
42
+
43
+ if (data.error) {
44
+ showMessage(uploadStatus, data.error, true);
45
+ imageSection.style.display = 'none';
46
+ analysisSection.style.display = 'none';
47
+ extractSection.style.display = 'none';
48
+ downloadSection.style.display = 'none';
49
+
50
+ } else {
51
+ chartPreview.src = data.image_url;
52
+ imageSection.style.display = 'block';
53
+ analysisSection.style.display = 'block';
54
+ extractSection.style.display = 'block';
55
+ downloadSection.style.display = 'none'; // Hide initially
56
+ showMessage(uploadStatus, 'Image uploaded successfully!');
57
+
58
+ }
59
+ } catch (error) {
60
+ showMessage(uploadStatus, 'An error occurred during upload.', true);
61
+ console.error('Upload error:', error);
62
+ imageSection.style.display = 'none';
63
+ analysisSection.style.display = 'none';
64
+ extractSection.style.display = 'none';
65
+ downloadSection.style.display = 'none';
66
+ }
67
+ });
68
+
69
+ // Handle analyze chart
70
+ analyzeButton.addEventListener('click', async function() {
71
+ clearMessage(analysisResults);
72
+
73
+ const query = queryInput.value;
74
+ const useCot = useCotCheckbox.checked;
75
+
76
+ if (!query) {
77
+ showMessage(analysisResults, 'Please enter a question.', true);
78
+ return;
79
+ }
80
+
81
+ const formData = new FormData();
82
+ formData.append('query', query);
83
+ formData.append('use_cot', useCot);
84
+
85
+ try {
86
+ const response = await fetch('/analyze', {
87
+ method: 'POST',
88
+ body: formData
89
+ });
90
+
91
+ const data = await response.json();
92
+
93
+ if (data.error) {
94
+ showMessage(analysisResults, data.error, true);
95
+ } else {
96
+ analysisResults.textContent = 'Answer: ' + data.answer;
97
+ analysisResults.style.color = 'black';
98
+ }
99
+ } catch (error) {
100
+ showMessage(analysisResults, 'An error occurred during analysis.', true);
101
+ console.error('Analysis error:', error);
102
+ }
103
+ });
104
+
105
+ // Handle extract data
106
+ extractButton.addEventListener('click', async function() {
107
+ clearMessage(extractStatus);
108
+ downloadSection.style.display = 'none'; // Hide until data is ready
109
+
110
+ try {
111
+ const response = await fetch('/extract', {
112
+ method: 'POST'
113
+ });
114
+
115
+ const data = await response.json();
116
+
117
+ if (data.error) {
118
+ showMessage(extractStatus, data.error, true);
119
+ } else {
120
+ // CSV data is in base64 format
121
+ const csvData = atob(data.csv_data); // Decode base64
122
+ const blob = new Blob([csvData], { type: 'text/csv' });
123
+ const url = URL.createObjectURL(blob);
124
+
125
+ downloadLink.href = url;
126
+ downloadLink.style.display = 'inline'; // Show the download link
127
+ downloadSection.style.display = 'block'; // Show the whole section
128
+
129
+ showMessage(extractStatus, 'Data extracted successfully!');
130
+ }
131
+ } catch (error) {
132
+ showMessage(extractStatus, 'An error occurred during extraction.', true);
133
+ console.error('Extraction error:', error);
134
+ }
135
+ });
136
+
137
+ });
static/uploads/539841_1_En_23_Fig6_HTML.png ADDED
static/uploads/benchmark.jpg ADDED

Git LFS Details

  • SHA256: 415acc7917a65f6bf96d8133c9de1f552ea8e4fde75d462be77ff574988e354e
  • Pointer size: 131 Bytes
  • Size of remote file: 320 kB
static/uploads/download (1).png ADDED
static/uploads/p0fcgbjj.png ADDED
templates/index.html ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Chart Analysis</title>
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
8
+ </head>
9
+ <body>
10
+ <h1>📊 Chart Analysis</h1>
11
+
12
+ <!-- Image Upload Section -->
13
+ <div id="uploadSection">
14
+ <form id="uploadForm" enctype="multipart/form-data">
15
+ <label for="chart">Upload Chart:</label>
16
+ <input type="file" id="chart" name="image" accept="image/*">
17
+ <button type="submit">Upload</button>
18
+ </form>
19
+ <div id="uploadStatus"></div>
20
+ </div>
21
+
22
+ <!-- Image Display Section -->
23
+ <div id="imageSection" style="display: none;">
24
+ <h2>Uploaded Chart:</h2>
25
+ <img id="chartPreview" src="" alt="Uploaded Chart" style="max-width: 100%; height: auto;">
26
+ </div>
27
+
28
+ <!-- Analysis Section -->
29
+ <div id="analysisSection" style="display: none;">
30
+ <h2>Analyze Chart</h2>
31
+ <label for="query">Ask a question:</label>
32
+ <input type="text" id="query" placeholder="E.g., What is the highest value?">
33
+ <br>
34
+ <input type="checkbox" id="use_cot">
35
+ <label for="use_cot">Enable Chain-of-Thought</label>
36
+ <button id="analyzeButton">Analyze</button>
37
+ <div id="analysisResults"></div>
38
+ </div>
39
+
40
+ <!-- Extract Data Section -->
41
+ <div id="extractSection" style="display: none;">
42
+ <h2>Extract Data</h2>
43
+ <button id="extractButton">Extract Data Points</button>
44
+ <div id="extractStatus"></div>
45
+ </div>
46
+
47
+ <!-- Download Section -->
48
+ <div id="downloadSection" style="display: none;">
49
+ <h2>Download Data</h2>
50
+ <a id="downloadLink" href="#" download="extracted_data.csv">Download CSV</a>
51
+ </div>
52
+
53
+ <script src="{{ url_for('static', filename='js/script.js') }}"></script>
54
+ </body>
55
+ </html>