bluestpanda commited on
Commit
6269828
·
1 Parent(s): 8d5dbab

Simplify UI design - minimal clean interface

Browse files

- Removed all emojis and decorative elements
- Shortened titles and labels
- Removed verbose debug messages
- Simplified tab names
- Removed unnecessary markdown headers
- Reduced text sizes and spacing
- Cleaned up buttons and inputs
- Removed example sections
- Minimalistic debug tab

Files changed (1) hide show
  1. src/streamlit_app.py +62 -273
src/streamlit_app.py CHANGED
@@ -18,23 +18,12 @@ st.set_page_config(
18
  layout="wide"
19
  )
20
 
21
- # Debug: Show Streamlit version and environment info
22
- with st.sidebar:
23
- st.info(f"🐍 Python {sys.version}")
24
- st.info(f"📦 Streamlit {st.__version__}")
25
-
26
- # Import our modules
27
- try:
28
- from structure_analysis import (
29
- detect_summary_fields,
30
- classify_data_structure,
31
- get_hierarchy_summary
32
- )
33
- st.success("✅ Successfully imported structure_analysis module")
34
- except ImportError as e:
35
- st.error(f"⚠️ Import error: {e}")
36
- st.error("⚠️ structure_analysis.py not found. Make sure all files are uploaded.")
37
- st.stop()
38
 
39
  # Session state
40
  if 'analysis_result' not in st.session_state:
@@ -174,13 +163,12 @@ def generate_regex_patterns(field_names: list, data_sample: dict, summary_sample
174
 
175
  def main():
176
  """Main application."""
177
- st.title("🤖 Field Correlation Analyzer")
178
- st.markdown("Upload a JSON file to analyze important fields and generate regex patterns")
179
 
180
- # Try alternative method for Hugging Face Spaces
181
  upload_method = st.radio(
182
- "Upload Method:",
183
- ["Standard Upload (recommended)", "Text Paste (alternative)"],
184
  horizontal=True,
185
  key="upload_method"
186
  )
@@ -188,21 +176,16 @@ def main():
188
  uploaded_file = None
189
  pasted_content = None
190
 
191
- if upload_method == "Standard Upload (recommended)":
192
- # File upload with accept_multiple_files for better debugging
193
  uploaded_file = st.file_uploader(
194
- "Choose a JSON file (Max size: 1 GB)",
195
  type=['json'],
196
- help="Upload a JSON file with structured data (Maximum file size: 1 GB)",
197
  key="json_file_uploader"
198
  )
199
  else:
200
- # Alternative: Allow pasting JSON content
201
- st.info("📋 Paste your JSON content below:")
202
  pasted_content = st.text_area(
203
- "JSON Content",
204
- height=200,
205
- help="Paste the full JSON content here",
206
  key="pasted_json"
207
  )
208
 
@@ -210,117 +193,44 @@ def main():
210
  content_str = None
211
  file_name = None
212
 
213
- if upload_method == "Text Paste (alternative)" and pasted_content:
214
- st.success("✅ Content pasted successfully!")
215
  content_str = pasted_content
216
  file_name = "pasted_content.json"
217
  elif uploaded_file is not None:
218
- st.success(f"✅ Upload widget triggered! File object received: {type(uploaded_file)}")
219
  file_name = uploaded_file.name
220
 
221
- # Debug upload widget state
222
- if upload_method == "Standard Upload (recommended)":
223
- st.info(f"🔍 Upload widget state: {uploaded_file is not None}")
224
-
225
- if uploaded_file is None:
226
- st.info("📝 Waiting for file upload...")
227
- st.warning("⚠️ If you've selected a file but see this message, the file isn't being processed by the backend")
228
-
229
  if content_str or uploaded_file is not None:
230
- # Debug file upload info
231
- if uploaded_file is not None:
232
- st.info(f"📁 File uploaded: {uploaded_file.name} (Size: {uploaded_file.size} bytes)")
233
- st.info(f"🔍 File type: {uploaded_file.type}")
234
- st.info(f"🔍 File ID: {uploaded_file.file_id}")
235
- else:
236
- st.info(f"📁 Content pasted: {file_name}")
237
-
238
- # Read and parse JSON
239
  try:
240
- if not content_str: # Only read from file if content_str not already set
241
- st.info("🔄 Reading file content...")
242
-
243
- # Reset file pointer in case it was read before
244
  uploaded_file.seek(0)
245
-
246
- # Read the file content
247
  content = uploaded_file.read()
248
- uploaded_file.seek(0) # Reset for potential re-reading
249
-
250
- st.info(f"📄 Content length: {len(content)} characters")
251
 
252
  if len(content) == 0:
253
- st.error("File appears to be empty!")
254
- st.warning("This might be a bug. Please try uploading the file again.")
255
  return
256
 
257
- # Try to decode as UTF-8
258
  try:
259
  content_str = content.decode('utf-8')
260
- except UnicodeDecodeError as e:
261
- st.error(f"File encoding error: {e}")
262
- st.error("Please ensure your JSON file is UTF-8 encoded")
263
  return
264
- else:
265
- st.info("🔄 Using pasted content...")
266
- st.info(f"📄 Content length: {len(content_str)} characters")
267
 
268
- st.info("🔄 Parsing JSON...")
269
  data = json.loads(content_str)
 
270
 
271
- st.success("✅ File loaded successfully!")
272
- st.info(f"📊 Data structure: {type(data)} with {len(data) if isinstance(data, (dict, list)) else 'unknown'} top-level items")
273
-
274
- # Debug tab for troubleshooting
275
- st.markdown("---")
276
- with st.expander("🐛 Debug Information (Click to expand)", expanded=False):
277
- col1, col2 = st.columns(2)
278
-
279
- with col1:
280
- st.markdown("#### Upload Details")
281
- if uploaded_file is not None:
282
- st.text(f"File Name: {uploaded_file.name}")
283
- st.text(f"File Size: {uploaded_file.size} bytes ({uploaded_file.size / 1024:.2f} KB)")
284
- st.text(f"File Type: {uploaded_file.type}")
285
- st.text(f"File ID: {uploaded_file.file_id}")
286
- else:
287
- st.text(f"Source: Pasted Content")
288
- st.text(f"File Name: {file_name}")
289
- st.text(f"Content Length: {len(content_str)} bytes ({len(content_str) / 1024:.2f} KB)")
290
- st.text(f"Encoding: UTF-8")
291
-
292
- with col2:
293
- st.markdown("#### Data Details")
294
- st.text(f"Content Length: {len(content_str)} bytes")
295
- st.text(f"Data Type: {type(data).__name__}")
296
- st.text(f"Top-level Keys: {list(data.keys())[:5] if isinstance(data, dict) else 'N/A'}")
297
- st.text(f"Encoding: UTF-8")
298
-
299
- # Sidebar for settings
300
  with st.sidebar:
301
- st.header("⚙️ Settings")
302
-
303
- # Target field input
304
- target_field = st.text_input(
305
- "Target Field",
306
- value="rotation_enabled",
307
- help="The field you want to analyze"
308
- )
309
 
310
- # Analyze button
311
- if st.button("🔍 Analyze", type="primary"):
312
- with st.spinner("Analyzing data structure..."):
313
- st.info(f"🎯 Analyzing with target field: {target_field}")
314
  try:
315
  analysis_result = analyze_with_llm(data, target_field)
316
  st.session_state.analysis_result = analysis_result
317
  st.session_state.data = data
318
- st.success("✅ Analysis completed successfully!")
319
  except Exception as e:
320
- st.error(f"Analysis failed: {e}")
321
- st.error(f"Error type: {type(e).__name__}")
322
- import traceback
323
- st.code(traceback.format_exc())
324
 
325
  # Display results if available
326
  if st.session_state.analysis_result:
@@ -339,59 +249,47 @@ def main():
339
 
340
  st.markdown("---")
341
 
342
- # Create tabs
343
  tab1, tab2, tab3, tab4, tab5 = st.tabs([
344
- "📊 Structure Analysis",
345
- "🎯 Field Recommendations",
346
- "📝 Generated Patterns",
347
- "📄 Raw Data",
348
- "🐛 Debug Log"
349
  ])
350
 
351
  with tab1:
352
- st.subheader("Data Hierarchy")
353
-
354
- # Summary fields
355
  if analysis['summary_fields_detected']:
356
- st.markdown("#### Level 1: Summary/Aggregate Fields (Highest Priority)")
357
  for field in analysis['summary_fields_detected'][:10]:
358
- st.write(f"`{field}`")
359
 
360
- # Config fields
361
  config_fields = analysis['classification'].get('config_fields', [])
362
  if config_fields:
363
- st.markdown("#### Level 2: Configuration/Compliance Fields")
364
  for field in config_fields[:10]:
365
- st.write(f"`{field}`")
366
 
367
- # Object arrays
368
  object_arrays = analysis['classification'].get('object_arrays', [])
369
  if object_arrays:
370
- st.markdown("#### Level 3: Object Arrays")
371
  for field in object_arrays[:5]:
372
- st.write(f"`{field}`")
373
 
374
- # Show sample data
375
- with st.expander("📋 View Summary Data Sample"):
376
  st.json(analysis['summary_sample'])
377
 
378
- with st.expander("📋 View Object Data Sample"):
379
  st.json(analysis['sample_object'])
380
 
381
  with tab2:
382
- st.subheader("Recommended Fields for Analysis")
383
-
384
  if analysis['recommended_fields']:
385
- st.info("These fields are recommended based on the data hierarchy and target field.")
386
-
387
- # Let user select fields
388
  selected_fields = st.multiselect(
389
- "Select fields to generate patterns for:",
390
  analysis['recommended_fields'],
391
  default=analysis['recommended_fields'][:3]
392
  )
393
 
394
- if selected_fields and st.button("Generate Patterns"):
395
  patterns = generate_regex_patterns(
396
  selected_fields,
397
  analysis['sample_object'],
@@ -402,169 +300,60 @@ def main():
402
  'fields': selected_fields,
403
  'patterns': patterns
404
  }
405
- else:
406
- st.warning("No recommended fields found.")
407
 
408
  with tab3:
409
  if 'generated_patterns' in st.session_state:
410
  patterns_data = st.session_state.generated_patterns
411
 
412
- st.subheader("Generated Regex Patterns")
413
-
414
- # Show patterns
415
- for i, (field, pattern) in enumerate(zip(patterns_data['fields'], patterns_data['patterns']), 1):
416
- st.markdown(f"**Pattern {i}: {field}**")
417
- st.code(pattern, language="regex", line_numbers=False)
418
- st.markdown("---")
419
 
420
- # Copy to clipboard
421
  all_patterns = "\n".join(patterns_data['patterns'])
422
- st.text_area(
423
- "All Patterns (copy this):",
424
- all_patterns,
425
- height=100
426
- )
427
 
428
- # JSON export
429
  export_data = {
430
- "test_name": "Field Analysis",
431
- "important_fields": patterns_data['fields'],
432
- "reasoning": "Fields identified using hierarchical analysis prioritizing summary/aggregate fields",
433
- "generated_regex": patterns_data['patterns']
434
  }
435
 
436
  st.download_button(
437
- label="📥 Download as JSON",
438
  data=json.dumps(export_data, indent=2),
439
- file_name="analysis_result.json",
440
  mime="application/json"
441
  )
442
- else:
443
- st.info("👆 Go to 'Field Recommendations' tab to select fields and generate patterns.")
444
 
445
  with tab4:
446
- st.subheader("Raw Data Structure")
447
-
448
- # Full data viewer
449
  st.json(data)
450
-
451
- # Download raw data
452
  st.download_button(
453
- label="📥 Download Raw Data",
454
  data=json.dumps(data, indent=2),
455
- file_name="raw_data.json",
456
  mime="application/json"
457
  )
458
 
459
  with tab5:
460
- st.subheader("🐛 Debug Information")
461
-
462
  col1, col2 = st.columns(2)
463
 
464
  with col1:
465
- st.markdown("#### File Upload Details")
466
- debug_info = {
467
- "File Name": uploaded_file.name if uploaded_file else "None",
468
- "File Size (bytes)": uploaded_file.size if uploaded_file else 0,
469
- "File Type": uploaded_file.type if uploaded_file else "None",
470
- "File ID": str(uploaded_file.file_id) if uploaded_file else "None",
471
- }
472
-
473
- for key, value in debug_info.items():
474
- st.text(f"{key}: {value}")
475
-
476
- # Server configuration
477
- st.markdown("#### Server Configuration")
478
- config_info = {
479
- "Python Version": sys.version.split('\n')[0],
480
- "Streamlit Version": st.__version__,
481
- "Upload Size Limit": "1024 MB (1 GB)",
482
- "XSRF Protection": "Disabled",
483
- }
484
-
485
- for key, value in config_info.items():
486
- st.text(f"{key}: {value}")
487
 
488
  with col2:
489
- st.markdown("#### Session State")
490
- session_keys = list(st.session_state.keys())
491
-
492
- if session_keys:
493
- for key in session_keys:
494
- value = st.session_state[key]
495
- if isinstance(value, (dict, list)):
496
- st.text(f"{key}: {type(value).__name__} ({len(value)} items)")
497
- else:
498
- st.text(f"{key}: {str(value)[:50]}...")
499
- else:
500
- st.info("No session state data")
501
-
502
- st.markdown("#### Analysis Metadata")
503
  if st.session_state.get('analysis_result'):
504
- analysis = st.session_state.analysis_result
505
- st.text(f"Summary Fields: {len(analysis.get('summary_fields_detected', []))}")
506
- st.text(f"Total Objects: {analysis.get('total_objects', 0)}")
507
- st.text(f"Recommended Fields: {len(analysis.get('recommended_fields', []))}")
508
- else:
509
- st.info("No analysis data yet")
510
-
511
- # Debug console output
512
- st.markdown("#### Backend Console Output")
513
- st.info("Check your browser console (F12) and Streamlit terminal for detailed backend logs")
514
-
515
- # Refresh button
516
- if st.button("🔄 Refresh Debug Info"):
517
- st.rerun()
518
 
519
  except json.JSONDecodeError as e:
520
- st.error(f"Invalid JSON file: {e}")
521
  except Exception as e:
522
- st.error(f"Error processing file: {e}")
523
- st.error(f"Error type: {type(e).__name__}")
524
- import traceback
525
- st.code(traceback.format_exc())
526
-
527
- else:
528
- # Show example when no file uploaded
529
- st.info("👆 Please upload a JSON file to begin analysis")
530
-
531
- with st.expander("📖 How to use"):
532
- st.markdown("""
533
- **Steps:**
534
- 1. Upload a JSON file with structured data
535
- 2. Set the target field you want to analyze (e.g., `rotation_enabled`)
536
- 3. Click "Analyze" to process the data
537
- 4. Review the structure analysis and field recommendations
538
- 5. Select fields and generate regex patterns
539
- 6. Download the results as JSON
540
-
541
- **What this tool does:**
542
- - Detects summary/aggregate fields automatically
543
- - Classifies data structure by hierarchy levels
544
- - Recommends important fields for validation
545
- - Generates regex patterns for field extraction
546
- """)
547
-
548
- with st.expander("📋 Example JSON Structure"):
549
- example = {
550
- "results": {
551
- "summary": {
552
- "total_keys": 13,
553
- "rotated_keys": 6,
554
- "rotation_percentage": 46
555
- },
556
- "kms_keys": {
557
- "object": [
558
- {
559
- "key_id": "12345",
560
- "rotation_enabled": True,
561
- "key_state": "Enabled"
562
- }
563
- ]
564
- }
565
- }
566
- }
567
- st.json(example)
568
 
569
 
570
  if __name__ == "__main__":
 
18
  layout="wide"
19
  )
20
 
21
+ # Import modules silently
22
+ from structure_analysis import (
23
+ detect_summary_fields,
24
+ classify_data_structure,
25
+ get_hierarchy_summary
26
+ )
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  # Session state
29
  if 'analysis_result' not in st.session_state:
 
163
 
164
  def main():
165
  """Main application."""
166
+ st.title("Field Analyzer")
 
167
 
168
+ # Upload method selection
169
  upload_method = st.radio(
170
+ "",
171
+ ["File Upload", "Text Paste"],
172
  horizontal=True,
173
  key="upload_method"
174
  )
 
176
  uploaded_file = None
177
  pasted_content = None
178
 
179
+ if upload_method == "File Upload":
 
180
  uploaded_file = st.file_uploader(
181
+ "Upload JSON file",
182
  type=['json'],
 
183
  key="json_file_uploader"
184
  )
185
  else:
 
 
186
  pasted_content = st.text_area(
187
+ "Paste JSON",
188
+ height=150,
 
189
  key="pasted_json"
190
  )
191
 
 
193
  content_str = None
194
  file_name = None
195
 
196
+ if upload_method == "Text Paste" and pasted_content:
 
197
  content_str = pasted_content
198
  file_name = "pasted_content.json"
199
  elif uploaded_file is not None:
 
200
  file_name = uploaded_file.name
201
 
 
 
 
 
 
 
 
 
202
  if content_str or uploaded_file is not None:
 
 
 
 
 
 
 
 
 
203
  try:
204
+ if not content_str:
205
+ # Read from uploaded file
 
 
206
  uploaded_file.seek(0)
 
 
207
  content = uploaded_file.read()
208
+ uploaded_file.seek(0)
 
 
209
 
210
  if len(content) == 0:
211
+ st.error("File is empty")
 
212
  return
213
 
 
214
  try:
215
  content_str = content.decode('utf-8')
216
+ except UnicodeDecodeError:
217
+ st.error("File encoding error")
 
218
  return
 
 
 
219
 
 
220
  data = json.loads(content_str)
221
+ st.success(f"Loaded: {file_name}")
222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  with st.sidebar:
224
+ target_field = st.text_input("Target Field", value="rotation_enabled")
 
 
 
 
 
 
 
225
 
226
+ if st.button("Analyze", type="primary"):
227
+ with st.spinner("Analyzing..."):
 
 
228
  try:
229
  analysis_result = analyze_with_llm(data, target_field)
230
  st.session_state.analysis_result = analysis_result
231
  st.session_state.data = data
 
232
  except Exception as e:
233
+ st.error(f"Analysis failed: {e}")
 
 
 
234
 
235
  # Display results if available
236
  if st.session_state.analysis_result:
 
249
 
250
  st.markdown("---")
251
 
 
252
  tab1, tab2, tab3, tab4, tab5 = st.tabs([
253
+ "Analysis",
254
+ "Fields",
255
+ "Patterns",
256
+ "Data",
257
+ "Debug"
258
  ])
259
 
260
  with tab1:
 
 
 
261
  if analysis['summary_fields_detected']:
262
+ st.write("**Summary Fields**")
263
  for field in analysis['summary_fields_detected'][:10]:
264
+ st.write(f"`{field}`")
265
 
 
266
  config_fields = analysis['classification'].get('config_fields', [])
267
  if config_fields:
268
+ st.write("**Config Fields**")
269
  for field in config_fields[:10]:
270
+ st.write(f"`{field}`")
271
 
 
272
  object_arrays = analysis['classification'].get('object_arrays', [])
273
  if object_arrays:
274
+ st.write("**Object Arrays**")
275
  for field in object_arrays[:5]:
276
+ st.write(f"`{field}`")
277
 
278
+ with st.expander("Summary Sample"):
 
279
  st.json(analysis['summary_sample'])
280
 
281
+ with st.expander("Object Sample"):
282
  st.json(analysis['sample_object'])
283
 
284
  with tab2:
 
 
285
  if analysis['recommended_fields']:
 
 
 
286
  selected_fields = st.multiselect(
287
+ "Select fields:",
288
  analysis['recommended_fields'],
289
  default=analysis['recommended_fields'][:3]
290
  )
291
 
292
+ if selected_fields and st.button("Generate"):
293
  patterns = generate_regex_patterns(
294
  selected_fields,
295
  analysis['sample_object'],
 
300
  'fields': selected_fields,
301
  'patterns': patterns
302
  }
 
 
303
 
304
  with tab3:
305
  if 'generated_patterns' in st.session_state:
306
  patterns_data = st.session_state.generated_patterns
307
 
308
+ for field, pattern in zip(patterns_data['fields'], patterns_data['patterns']):
309
+ st.write(f"**{field}**")
310
+ st.code(pattern)
311
+ st.write("")
 
 
 
312
 
 
313
  all_patterns = "\n".join(patterns_data['patterns'])
314
+ st.text_area("All Patterns:", all_patterns, height=100)
 
 
 
 
315
 
 
316
  export_data = {
317
+ "fields": patterns_data['fields'],
318
+ "patterns": patterns_data['patterns']
 
 
319
  }
320
 
321
  st.download_button(
322
+ "Download JSON",
323
  data=json.dumps(export_data, indent=2),
324
+ file_name="analysis.json",
325
  mime="application/json"
326
  )
 
 
327
 
328
  with tab4:
 
 
 
329
  st.json(data)
 
 
330
  st.download_button(
331
+ "Download Raw",
332
  data=json.dumps(data, indent=2),
333
+ file_name="raw.json",
334
  mime="application/json"
335
  )
336
 
337
  with tab5:
 
 
338
  col1, col2 = st.columns(2)
339
 
340
  with col1:
341
+ st.write("**Upload**")
342
+ st.text(f"File: {uploaded_file.name if uploaded_file else 'N/A'}")
343
+ st.text(f"Size: {uploaded_file.size if uploaded_file else 0} bytes")
344
+ st.text(f"Streamlit: {st.__version__}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
 
346
  with col2:
347
+ st.write("**Analysis**")
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  if st.session_state.get('analysis_result'):
349
+ a = st.session_state.analysis_result
350
+ st.text(f"Fields: {len(a.get('summary_fields_detected', []))}")
351
+ st.text(f"Objects: {a.get('total_objects', 0)}")
 
 
 
 
 
 
 
 
 
 
 
352
 
353
  except json.JSONDecodeError as e:
354
+ st.error(f"Invalid JSON: {e}")
355
  except Exception as e:
356
+ st.error(f"Error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
 
358
 
359
  if __name__ == "__main__":