Fanwang Meng commited on
Commit
0e47f57
·
1 Parent(s): f705b42

Adapt to HF

Browse files
Files changed (2) hide show
  1. app.py +53 -15
  2. templates/index.html +136 -216
app.py CHANGED
@@ -29,8 +29,22 @@ ALLOWED_EXTENSIONS = {"txt", "npz", "xlsx", "xls"}
29
  app = Flask(__name__)
30
  app.config["MAX_CONTENT_LENGTH"] = 32 * 1024 * 1024 # 32MB max file size
31
  app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
 
32
  file_lock = threading.Lock()
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  # Ensure upload directory exists
35
  os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
36
 
@@ -297,34 +311,50 @@ def upload_selection_file():
297
  """Handle file upload and process selection."""
298
  try:
299
  print("Debug - Starting upload_selection_file")
 
 
300
 
301
  if "file" not in request.files:
302
- return create_json_response({"error": "No file provided"}, 400)
303
 
304
  file = request.files["file"]
305
  if file.filename == "":
306
- return create_json_response({"error": "No file selected"}, 400)
307
 
308
  if not allowed_file(file.filename):
309
- return create_json_response({"error": "File type not allowed"}, 400)
310
 
311
- # Get parameters
312
  algorithm = request.form.get("algorithm")
 
313
  if not algorithm:
314
- return create_json_response({"error": "No algorithm specified"}, 400)
315
 
316
  # Get size parameter
317
  size = request.form.get("size")
 
318
  if not size:
319
- return create_json_response({"error": "Subset size must be specified"}, 400)
 
 
 
 
 
 
 
320
 
321
  # Get distance function
322
  dist_metric = request.form.get("func_dist", "")
 
323
 
324
  # Parse parameters
325
  try:
326
- parameters = orjson.loads(request.form.get("parameters", "{}"))
 
 
 
327
  except Exception as e:
 
328
  parameters = {}
329
 
330
  # Add size to parameters
@@ -332,34 +362,43 @@ def upload_selection_file():
332
 
333
  # Create a unique directory for this upload
334
  upload_dir = get_unique_upload_dir()
 
335
 
336
  try:
337
  # Save file with unique name
338
- file_path = os.path.join(
339
- upload_dir, secure_filename(str(uuid.uuid4()) + "_" + file.filename)
340
- )
341
 
342
  with file_lock:
343
  file.save(file_path)
344
- # os.chmod(file_path, 0o666) # Read/write for all
 
345
 
346
  # Load data
 
347
  array = load_data(file_path)
 
348
 
349
- # Process the selection with separate dist_metric parameter
 
350
  result = process_selection(array, algorithm, parameters, dist_metric)
 
351
 
352
  return create_json_response(result)
353
 
354
  except Exception as e:
355
- return create_json_response({"error": str(e)}, 500)
 
356
 
357
  finally:
358
  # Clean up the unique upload directory
 
359
  clean_upload_dir(upload_dir)
360
 
361
  except Exception as e:
362
- return create_json_response({"error": f"Error processing request: {str(e)}"}, 400)
 
363
 
364
 
365
  @app.route("/download", methods=["POST"])
@@ -513,4 +552,3 @@ def health_check():
513
 
514
  if __name__ == "__main__":
515
  app.run(debug=False, host="0.0.0.0", port=7860)
516
-
 
29
  app = Flask(__name__)
30
  app.config["MAX_CONTENT_LENGTH"] = 32 * 1024 * 1024 # 32MB max file size
31
  app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
32
+ app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 0 # Disable caching
33
  file_lock = threading.Lock()
34
 
35
+ # Configure for Hugging Face environment
36
+ app.config["PREFERRED_URL_SCHEME"] = "https"
37
+ app.config["SESSION_COOKIE_SECURE"] = True
38
+ app.config["SESSION_COOKIE_SAMESITE"] = "None"
39
+
40
+ # Set CORS headers for all responses
41
+ @app.after_request
42
+ def after_request(response):
43
+ response.headers.add('Access-Control-Allow-Origin', '*')
44
+ response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
45
+ response.headers.add('Access-Control-Allow-Methods', 'GET,POST,OPTIONS')
46
+ return response
47
+
48
  # Ensure upload directory exists
49
  os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
50
 
 
311
  """Handle file upload and process selection."""
312
  try:
313
  print("Debug - Starting upload_selection_file")
314
+ print("Debug - Request files:", request.files)
315
+ print("Debug - Request form:", request.form)
316
 
317
  if "file" not in request.files:
318
+ return create_json_response({"error": "No file provided", "debug": "file not in request.files"}, 400)
319
 
320
  file = request.files["file"]
321
  if file.filename == "":
322
+ return create_json_response({"error": "No file selected", "debug": "empty filename"}, 400)
323
 
324
  if not allowed_file(file.filename):
325
+ return create_json_response({"error": "File type not allowed", "debug": f"invalid file type: {file.filename}"}, 400)
326
 
327
+ # Get parameters with debug logging
328
  algorithm = request.form.get("algorithm")
329
+ print("Debug - Algorithm:", algorithm)
330
  if not algorithm:
331
+ return create_json_response({"error": "No algorithm specified", "debug": "algorithm not in form"}, 400)
332
 
333
  # Get size parameter
334
  size = request.form.get("size")
335
+ print("Debug - Size:", size)
336
  if not size:
337
+ return create_json_response({"error": "Subset size must be specified", "debug": "size not in form"}, 400)
338
+
339
+ try:
340
+ size = int(size)
341
+ if size < 1:
342
+ raise ValueError("Size must be positive")
343
+ except ValueError as e:
344
+ return create_json_response({"error": f"Invalid size value: {str(e)}", "debug": f"size validation failed: {size}"}, 400)
345
 
346
  # Get distance function
347
  dist_metric = request.form.get("func_dist", "")
348
+ print("Debug - Distance metric:", dist_metric)
349
 
350
  # Parse parameters
351
  try:
352
+ parameters_str = request.form.get("parameters", "{}")
353
+ print("Debug - Parameters string:", parameters_str)
354
+ parameters = orjson.loads(parameters_str)
355
+ print("Debug - Parsed parameters:", parameters)
356
  except Exception as e:
357
+ print("Debug - Parameter parsing error:", str(e))
358
  parameters = {}
359
 
360
  # Add size to parameters
 
362
 
363
  # Create a unique directory for this upload
364
  upload_dir = get_unique_upload_dir()
365
+ print("Debug - Upload directory:", upload_dir)
366
 
367
  try:
368
  # Save file with unique name
369
+ filename = secure_filename(str(uuid.uuid4()) + "_" + file.filename)
370
+ file_path = os.path.join(upload_dir, filename)
371
+ print("Debug - File path:", file_path)
372
 
373
  with file_lock:
374
  file.save(file_path)
375
+ os.chmod(file_path, 0o666) # Read/write for all
376
+ print("Debug - File saved successfully")
377
 
378
  # Load data
379
+ print("Debug - Loading data from file")
380
  array = load_data(file_path)
381
+ print("Debug - Data loaded, shape:", array.shape)
382
 
383
+ # Process the selection
384
+ print("Debug - Processing selection")
385
  result = process_selection(array, algorithm, parameters, dist_metric)
386
+ print("Debug - Selection processed")
387
 
388
  return create_json_response(result)
389
 
390
  except Exception as e:
391
+ print("Debug - Processing error:", str(e))
392
+ return create_json_response({"error": str(e), "debug": "processing error"}, 500)
393
 
394
  finally:
395
  # Clean up the unique upload directory
396
+ print("Debug - Cleaning up upload directory")
397
  clean_upload_dir(upload_dir)
398
 
399
  except Exception as e:
400
+ print("Debug - Unexpected error:", str(e))
401
+ return create_json_response({"error": f"Error processing request: {str(e)}", "debug": "unexpected error"}, 400)
402
 
403
 
404
  @app.route("/download", methods=["POST"])
 
552
 
553
  if __name__ == "__main__":
554
  app.run(debug=False, host="0.0.0.0", port=7860)
 
templates/index.html CHANGED
@@ -421,6 +421,8 @@
421
  </script>
422
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_HTML"></script>
423
  <script>
 
 
424
  // Initialize layout on page load
425
  document.addEventListener('DOMContentLoaded', function() {
426
  // Handle iframe environment
@@ -430,25 +432,63 @@
430
  document.body.style.height = '100%';
431
  }
432
 
 
 
 
433
  // Show initial section
434
  showSection('selection');
435
-
436
  // Update parameters for default algorithm
437
  updateDefaultParams();
438
-
439
  // Load markdown content
440
  loadMarkdownContent();
441
 
442
- // Ensure proper layout in iframe
443
- window.addEventListener('resize', function() {
444
- if (window.self !== window.top) {
445
- document.documentElement.style.height = '100%';
446
- document.body.style.height = '100%';
447
- }
448
- });
449
  });
450
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  function showSection(sectionId) {
 
452
  // Hide all sections
453
  document.querySelectorAll('.section').forEach(section => {
454
  section.style.display = 'none';
@@ -458,160 +498,24 @@
458
  const selectedSection = document.getElementById(sectionId);
459
  if (selectedSection) {
460
  selectedSection.style.display = 'block';
 
461
 
462
  // Update active nav link
463
  document.querySelectorAll('.nav-link').forEach(link => {
464
  link.classList.remove('active');
465
- if (link.getAttribute('href') === '#' + sectionId) {
466
  link.classList.add('active');
467
  }
468
  });
469
 
470
  // Reset scroll position
471
- if (selectedSection.scrollTo) {
472
- selectedSection.scrollTo(0, 0);
473
- }
474
-
475
- // Force layout recalculation in iframe
476
- if (window.self !== window.top) {
477
- selectedSection.style.height = '100%';
478
- selectedSection.style.overflow = 'auto';
479
- }
480
  }
481
  }
482
 
483
- // Sidebar toggle functionality
484
- const sidebar = document.getElementById('sidebar');
485
- const content = document.getElementById('content');
486
- const sidebarCollapse = document.getElementById('sidebarCollapse');
487
- const showSidebarBtn = document.getElementById('showSidebarBtn');
488
-
489
- function toggleSidebar() {
490
- sidebar.classList.toggle('active');
491
- content.classList.toggle('active');
492
- showSidebarBtn.classList.toggle('show', sidebar.classList.contains('active'));
493
- }
494
-
495
- sidebarCollapse.addEventListener('click', toggleSidebar);
496
- showSidebarBtn.addEventListener('click', toggleSidebar);
497
-
498
- // Handle navigation clicks
499
- document.querySelectorAll('.nav-link').forEach(link => {
500
- link.addEventListener('click', (e) => {
501
- e.preventDefault();
502
- const sectionId = link.getAttribute('data-section');
503
- showSection(sectionId);
504
- });
505
- });
506
-
507
- async function updateDefaultParams() {
508
- const algorithm = document.getElementById('algorithm').value;
509
- const parametersTextarea = document.getElementById('parameters');
510
- const nbinsInput = document.getElementById('nbins_axis');
511
-
512
- try {
513
- const response = await fetch(`/get_default_params/${algorithm}`);
514
- const data = await response.json();
515
-
516
- if (response.ok) {
517
- // Get base parameters
518
- const params = data;
519
-
520
- // Add nbins_axis for GridPartition
521
- if (algorithm === 'GridPartition') {
522
- params.nbins_axis = parseInt(nbinsInput.value) || 5;
523
- }
524
-
525
- // Format the parameters nicely
526
- parametersTextarea.value = JSON.stringify(params, null, 2);
527
- } else {
528
- console.error('Error getting default parameters:', data.error);
529
- parametersTextarea.value = '{}';
530
- }
531
- } catch (error) {
532
- console.error('Error:', error);
533
- parametersTextarea.value = '{}';
534
- }
535
- }
536
-
537
- // Update parameters when algorithm changes
538
- document.getElementById('algorithm').addEventListener('change', function() {
539
- const algorithm = this.value;
540
- const nbinsContainer = document.getElementById('nbins_axis_container');
541
-
542
- // Show/hide nbins_axis input based on algorithm
543
- if (algorithm === 'GridPartition') {
544
- nbinsContainer.style.display = 'block';
545
- } else {
546
- nbinsContainer.style.display = 'none';
547
- }
548
-
549
- updateDefaultParams();
550
- });
551
-
552
- // Update parameters when nbins_axis changes
553
- document.getElementById('nbins_axis').addEventListener('input', function() {
554
- if (document.getElementById('algorithm').value === 'GridPartition') {
555
- const params = JSON.parse(document.getElementById('parameters').value || '{}');
556
- params.nbins_axis = parseInt(this.value) || 5;
557
- document.getElementById('parameters').value = JSON.stringify(params, null, 2);
558
- }
559
- });
560
-
561
- // Update parameters when page loads
562
- document.addEventListener('DOMContentLoaded', function() {
563
- updateDefaultParams();
564
- // Also trigger algorithm change to show/hide nbins_axis input
565
- const algorithmSelect = document.getElementById('algorithm');
566
- algorithmSelect.dispatchEvent(new Event('change'));
567
- });
568
-
569
- async function downloadIndices() {
570
- if (!currentIndices) {
571
- console.error('No indices available for download');
572
- return;
573
- }
574
-
575
- const format = document.getElementById('download-format').value;
576
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
577
-
578
- try {
579
- const response = await fetch('/download', {
580
- method: 'POST',
581
- headers: {
582
- 'Content-Type': 'application/json',
583
- },
584
- body: JSON.stringify({
585
- indices: currentIndices,
586
- format: format,
587
- timestamp: timestamp
588
- })
589
- });
590
-
591
- if (response.ok) {
592
- // Create a download link
593
- const blob = await response.blob();
594
- const url = window.URL.createObjectURL(blob);
595
- const a = document.createElement('a');
596
- a.href = url;
597
- a.download = `selected_indices_${timestamp}.${format}`;
598
- document.body.appendChild(a);
599
- a.click();
600
- window.URL.revokeObjectURL(url);
601
- a.remove();
602
- } else {
603
- const error = await response.json();
604
- console.error('Download failed:', error);
605
- alert('Failed to download results: ' + (error.error || 'Unknown error'));
606
- }
607
- } catch (error) {
608
- console.error('Download error:', error);
609
- alert('Error downloading results: ' + error.message);
610
- }
611
- }
612
-
613
- document.getElementById('selectionForm').addEventListener('submit', async (e) => {
614
  e.preventDefault();
 
615
 
616
  const formData = new FormData(e.target);
617
  const resultSection = document.getElementById('resultSection');
@@ -620,40 +524,30 @@
620
  const successContent = document.getElementById('success-content');
621
  const indicesDiv = document.getElementById('indices');
622
 
623
- // Validate size is an integer
624
- const size = parseInt(formData.get('size'));
625
- if (isNaN(size) || size < 1 || size !== parseFloat(formData.get('size'))) {
626
- errorDiv.textContent = 'Size must be a positive integer';
627
- resultSection.style.display = 'block';
628
- return;
629
- }
630
-
631
  try {
 
632
  const response = await fetch('/upload_selection', {
633
  method: 'POST',
634
  body: formData
635
  });
636
 
637
  const data = await response.json();
638
- console.log('Response data:', data);
639
-
640
- // Reset display
641
- resultSection.style.display = 'block';
642
- warningsDiv.style.display = 'none';
643
- errorDiv.style.display = 'none';
644
- successContent.style.display = 'none';
645
 
646
  if (response.ok && data.success) {
647
- // Store the indices globally
648
  currentIndices = data.indices;
649
-
650
- // Show warnings if any
651
  if (data.warnings && data.warnings.length > 0) {
652
  warningsDiv.textContent = 'Warnings: ' + data.warnings.join(', ');
653
  warningsDiv.style.display = 'block';
654
  }
655
 
656
- // Show results
657
  indicesDiv.textContent = JSON.stringify(data.indices, null, 2);
658
  successContent.style.display = 'block';
659
  } else {
@@ -661,42 +555,20 @@
661
  errorDiv.style.display = 'block';
662
  }
663
  } catch (error) {
664
- console.error('Error:', error);
665
  errorDiv.textContent = 'Error processing request: ' + error.message;
666
  errorDiv.style.display = 'block';
667
  }
668
- });
669
-
670
- // Load markdown content and add MathJax support
671
- async function loadMarkdownContent() {
672
- try {
673
- // Load about content
674
- const aboutResponse = await fetch('/md/about.md');
675
- const aboutData = await aboutResponse.json();
676
- document.getElementById('about-content').innerHTML = aboutData.html;
677
-
678
- // Load contact content
679
- const contactResponse = await fetch('/md/contacts.md');
680
- const contactData = await contactResponse.json();
681
- document.getElementById('contact-content').innerHTML = contactData.html;
682
-
683
- // Retypeset math
684
- if (window.MathJax && window.MathJax.Hub) {
685
- window.MathJax.Hub.Queue(["Typeset", window.MathJax.Hub]);
686
- }
687
- } catch (error) {
688
- console.error('Error loading markdown content:', error);
689
- }
690
  }
691
 
692
- document.getElementById('diversity-form').addEventListener('submit', async function(e) {
693
  e.preventDefault();
 
694
 
695
- const formData = new FormData(this);
696
  const parameters = document.getElementById('div_parameters').value;
697
 
698
  try {
699
- // Parse and add JSON parameters
700
  const params = JSON.parse(parameters || '{}');
701
  formData.set('div_parameters', JSON.stringify(params));
702
 
@@ -706,39 +578,87 @@
706
  });
707
 
708
  const result = await response.json();
 
709
 
710
  if (response.ok) {
711
- // Show results
712
  document.getElementById('diversity-results').style.display = 'block';
713
  document.getElementById('diversity-score').textContent = result.diversity_score;
714
  } else {
715
  alert('Error: ' + result.error);
716
  }
717
  } catch (error) {
718
- console.error('Error:', error);
719
  alert('Error calculating diversity: ' + error.message);
720
  }
721
- });
722
 
723
- // Set default parameters when diversity type changes
724
- document.getElementById('div_type').addEventListener('change', function() {
725
- const defaultParams = {
726
- normalize: false,
727
- truncation: false,
728
- cs: null
729
- };
730
- document.getElementById('div_parameters').value = JSON.stringify(defaultParams, null, 2);
731
- });
732
 
733
- // Initialize default parameters
734
- document.addEventListener('DOMContentLoaded', function() {
735
- const defaultParams = {
736
- normalize: false,
737
- truncation: false,
738
- cs: null
739
- };
740
- document.getElementById('div_parameters').value = JSON.stringify(defaultParams, null, 2);
741
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
742
  </script>
743
  </head>
744
  <body>
 
421
  </script>
422
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_HTML"></script>
423
  <script>
424
+ let currentIndices = null; // Store the current indices globally
425
+
426
  // Initialize layout on page load
427
  document.addEventListener('DOMContentLoaded', function() {
428
  // Handle iframe environment
 
432
  document.body.style.height = '100%';
433
  }
434
 
435
+ // Initialize navigation
436
+ initializeNavigation();
437
+
438
  // Show initial section
439
  showSection('selection');
440
+
441
  // Update parameters for default algorithm
442
  updateDefaultParams();
443
+
444
  // Load markdown content
445
  loadMarkdownContent();
446
 
447
+ // Initialize form handlers
448
+ initializeFormHandlers();
 
 
 
 
 
449
  });
450
 
451
+ function initializeNavigation() {
452
+ // Handle navigation clicks
453
+ document.querySelectorAll('.nav-link').forEach(link => {
454
+ link.addEventListener('click', function(e) {
455
+ e.preventDefault();
456
+ const sectionId = this.getAttribute('data-section');
457
+ if (sectionId) {
458
+ showSection(sectionId);
459
+ }
460
+ });
461
+ });
462
+ }
463
+
464
+ function initializeFormHandlers() {
465
+ // Selection form handler
466
+ const selectionForm = document.getElementById('selectionForm');
467
+ if (selectionForm) {
468
+ selectionForm.addEventListener('submit', handleSelectionSubmit);
469
+ }
470
+
471
+ // Diversity form handler
472
+ const diversityForm = document.getElementById('diversity-form');
473
+ if (diversityForm) {
474
+ diversityForm.addEventListener('submit', handleDiversitySubmit);
475
+ }
476
+
477
+ // Algorithm change handler
478
+ const algorithmSelect = document.getElementById('algorithm');
479
+ if (algorithmSelect) {
480
+ algorithmSelect.addEventListener('change', handleAlgorithmChange);
481
+ }
482
+
483
+ // nbins_axis change handler
484
+ const nbinsInput = document.getElementById('nbins_axis');
485
+ if (nbinsInput) {
486
+ nbinsInput.addEventListener('input', handleNbinsChange);
487
+ }
488
+ }
489
+
490
  function showSection(sectionId) {
491
+ console.log('Showing section:', sectionId);
492
  // Hide all sections
493
  document.querySelectorAll('.section').forEach(section => {
494
  section.style.display = 'none';
 
498
  const selectedSection = document.getElementById(sectionId);
499
  if (selectedSection) {
500
  selectedSection.style.display = 'block';
501
+ console.log('Section displayed:', sectionId);
502
 
503
  // Update active nav link
504
  document.querySelectorAll('.nav-link').forEach(link => {
505
  link.classList.remove('active');
506
+ if (link.getAttribute('data-section') === sectionId) {
507
  link.classList.add('active');
508
  }
509
  });
510
 
511
  // Reset scroll position
512
+ selectedSection.scrollTop = 0;
 
 
 
 
 
 
 
 
513
  }
514
  }
515
 
516
+ async function handleSelectionSubmit(e) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
  e.preventDefault();
518
+ console.log('Selection form submitted');
519
 
520
  const formData = new FormData(e.target);
521
  const resultSection = document.getElementById('resultSection');
 
524
  const successContent = document.getElementById('success-content');
525
  const indicesDiv = document.getElementById('indices');
526
 
527
+ // Show loading state
528
+ resultSection.style.display = 'block';
529
+ warningsDiv.style.display = 'none';
530
+ errorDiv.style.display = 'none';
531
+ successContent.style.display = 'none';
532
+
 
 
533
  try {
534
+ console.log('Sending selection request...');
535
  const response = await fetch('/upload_selection', {
536
  method: 'POST',
537
  body: formData
538
  });
539
 
540
  const data = await response.json();
541
+ console.log('Selection response:', data);
 
 
 
 
 
 
542
 
543
  if (response.ok && data.success) {
 
544
  currentIndices = data.indices;
545
+
 
546
  if (data.warnings && data.warnings.length > 0) {
547
  warningsDiv.textContent = 'Warnings: ' + data.warnings.join(', ');
548
  warningsDiv.style.display = 'block';
549
  }
550
 
 
551
  indicesDiv.textContent = JSON.stringify(data.indices, null, 2);
552
  successContent.style.display = 'block';
553
  } else {
 
555
  errorDiv.style.display = 'block';
556
  }
557
  } catch (error) {
558
+ console.error('Selection error:', error);
559
  errorDiv.textContent = 'Error processing request: ' + error.message;
560
  errorDiv.style.display = 'block';
561
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  }
563
 
564
+ async function handleDiversitySubmit(e) {
565
  e.preventDefault();
566
+ console.log('Diversity form submitted');
567
 
568
+ const formData = new FormData(e.target);
569
  const parameters = document.getElementById('div_parameters').value;
570
 
571
  try {
 
572
  const params = JSON.parse(parameters || '{}');
573
  formData.set('div_parameters', JSON.stringify(params));
574
 
 
578
  });
579
 
580
  const result = await response.json();
581
+ console.log('Diversity response:', result);
582
 
583
  if (response.ok) {
 
584
  document.getElementById('diversity-results').style.display = 'block';
585
  document.getElementById('diversity-score').textContent = result.diversity_score;
586
  } else {
587
  alert('Error: ' + result.error);
588
  }
589
  } catch (error) {
590
+ console.error('Diversity error:', error);
591
  alert('Error calculating diversity: ' + error.message);
592
  }
593
+ }
594
 
595
+ function handleAlgorithmChange() {
596
+ const algorithm = this.value;
597
+ const nbinsContainer = document.getElementById('nbins_axis_container');
 
 
 
 
 
 
598
 
599
+ console.log('Algorithm changed to:', algorithm);
600
+
601
+ if (algorithm === 'GridPartition') {
602
+ nbinsContainer.style.display = 'block';
603
+ } else {
604
+ nbinsContainer.style.display = 'none';
605
+ }
606
+
607
+ updateDefaultParams();
608
+ }
609
+
610
+ function handleNbinsChange() {
611
+ if (document.getElementById('algorithm').value === 'GridPartition') {
612
+ const params = JSON.parse(document.getElementById('parameters').value || '{}');
613
+ params.nbins_axis = parseInt(this.value) || 5;
614
+ document.getElementById('parameters').value = JSON.stringify(params, null, 2);
615
+ }
616
+ }
617
+
618
+ async function updateDefaultParams() {
619
+ const algorithm = document.getElementById('algorithm').value;
620
+ const parametersTextarea = document.getElementById('parameters');
621
+ const nbinsInput = document.getElementById('nbins_axis');
622
+
623
+ try {
624
+ console.log('Fetching default params for:', algorithm);
625
+ const response = await fetch(`/get_default_params/${algorithm}`);
626
+ const data = await response.json();
627
+
628
+ if (response.ok) {
629
+ const params = data;
630
+ if (algorithm === 'GridPartition') {
631
+ params.nbins_axis = parseInt(nbinsInput.value) || 5;
632
+ }
633
+ parametersTextarea.value = JSON.stringify(params, null, 2);
634
+ } else {
635
+ console.error('Error getting default parameters:', data.error);
636
+ parametersTextarea.value = '{}';
637
+ }
638
+ } catch (error) {
639
+ console.error('Default params error:', error);
640
+ parametersTextarea.value = '{}';
641
+ }
642
+ }
643
+
644
+ async function loadMarkdownContent() {
645
+ try {
646
+ console.log('Loading markdown content...');
647
+ const aboutResponse = await fetch('/md/about.md');
648
+ const aboutData = await aboutResponse.json();
649
+ document.getElementById('about-content').innerHTML = aboutData.html;
650
+
651
+ const contactResponse = await fetch('/md/contacts.md');
652
+ const contactData = await contactResponse.json();
653
+ document.getElementById('contact-content').innerHTML = contactData.html;
654
+
655
+ if (window.MathJax && window.MathJax.Hub) {
656
+ window.MathJax.Hub.Queue(["Typeset", window.MathJax.Hub]);
657
+ }
658
+ } catch (error) {
659
+ console.error('Markdown loading error:', error);
660
+ }
661
+ }
662
  </script>
663
  </head>
664
  <body>