mnoorchenar commited on
Commit
2c0b974
Β·
1 Parent(s): a9e370b

Update 2026-01-30 19:35:53

Browse files
Files changed (2) hide show
  1. app.py +125 -183
  2. templates/index.html +278 -96
app.py CHANGED
@@ -3,10 +3,11 @@ Flask Application for Reference Management Pipeline
3
  Complete implementation matching overleaf.py functionality
4
  Includes LaTeX citation parsing, BibTeX processing, and full pipeline
5
 
6
- UPDATES:
7
  - Fixed abbreviations to include periods (ISO 4 standard): "Energy" β†’ "Ener."
8
- - Added endpoint to clear entire database
9
- - Better abbreviation rules for journal names
 
10
  """
11
 
12
  from flask import Flask, render_template, request, jsonify, send_file
@@ -78,7 +79,7 @@ def check_api_key():
78
  # =====================================================================
79
 
80
  def parse_citations_from_tex(tex_content: str) -> pd.DataFrame:
81
- """Parse citations from LaTeX content"""
82
  print("πŸ“– Parsing citations from LaTeX")
83
 
84
  lines = tex_content.split('\n')
@@ -559,20 +560,30 @@ def index():
559
 
560
  @app.route('/api/process', methods=['POST'])
561
  def process_bibtex():
562
- """Process BibTeX content (text input - original functionality)"""
563
  if not check_api_key():
564
  return jsonify({'error': 'Unauthorized'}), 401
565
 
566
  try:
567
- data = request.get_json()
568
-
569
- # Handle both 'bibtex' and 'bibtex_content' field names (for HTML compatibility)
570
- bibtex_content = data.get('bibtex_content') or data.get('bibtex', '')
571
- input_mode = data.get('input_mode', 'bibtex')
572
- enrich = data.get('enrich', False)
573
- abbreviate = data.get('abbreviate', False)
574
- protect = data.get('protect', False)
575
- save_to_db = data.get('save_to_db', False)
 
 
 
 
 
 
 
 
 
 
576
 
577
  print(f"πŸ“₯ Received: {len(bibtex_content)} chars, mode={input_mode}")
578
 
@@ -589,14 +600,12 @@ def process_bibtex():
589
  for i, title in enumerate(titles, 1):
590
  print(f" [{i}/{len(titles)}] Searching: {title[:50]}...")
591
  try:
592
- # Search Crossref
593
  url = f"https://api.crossref.org/works?query.bibliographic={requests.utils.quote(title)}&rows=1"
594
  response = HTTP.get(url, timeout=15)
595
  items = response.json().get("message", {}).get("items", [])
596
 
597
  if items and items[0].get('DOI'):
598
  doi = items[0]['DOI']
599
- # Fetch BibTeX
600
  bibtex_r = HTTP.get(
601
  f"https://doi.org/{doi}",
602
  headers={"Accept": "application/x-bibtex"},
@@ -610,22 +619,39 @@ def process_bibtex():
610
  except Exception as e:
611
  print(f" ⚠️ Failed: {e}")
612
 
613
- # Join all found entries
614
  bibtex_content = '\n\n'.join(bibtex_entries)
615
  print(f"βœ… Retrieved {len(bibtex_entries)} BibTeX entries from titles")
616
 
617
  if not bibtex_content:
618
  return jsonify({'error': 'No BibTeX entries found for the provided titles'}), 400
619
 
620
- # Normal BibTeX mode validation
621
  if not bibtex_content or len(bibtex_content.strip()) == 0:
622
  return jsonify({'error': 'No BibTeX content provided'}), 400
623
 
 
624
  df = parse_bibtex_input(bibtex_content)
625
 
626
  if df.empty:
627
  return jsonify({'error': 'No valid BibTeX entries found'}), 400
628
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
  # Enrich if requested
630
  if enrich:
631
  df = enrich_with_crossref(df)
@@ -646,28 +672,33 @@ def process_bibtex():
646
  for _, row in df.iterrows():
647
  doi = row.get('DOI', '').strip()
648
  year_int = extract_year_int(row.get('Year', ''))
 
649
 
650
  try:
651
  cursor.execute('''
652
  INSERT OR REPLACE INTO bibliography
653
- (session_id, key, doi, type, authors, title, journal_booktitle, year, year_int,
654
- publisher, volume, pages, bibtex, crossref_bibtex, crossref_bibtex_localkey,
655
- title_similarity, journal_abbreviation, crossref_bibtex_abbrev,
656
- crossref_bibtex_protected, imported_date)
657
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
658
  ''', (
659
- session_id, row['Key'], doi, row['Type'], row['Authors'],
660
- row['Title'], row['Journal/Booktitle'], row['Year'], year_int,
661
- row['Publisher'], row.get('Volume', ''), row.get('Pages', ''),
662
- row['BibTeX'], row.get('Crossref_BibTeX', row['BibTeX']),
663
- row.get('Crossref_BibTeX_LocalKey', row['BibTeX']),
 
 
 
664
  row.get('Title_Similarity', 0), row.get('Journal_Abbreviation', ''),
665
- row.get('Crossref_BibTeX_Abbrev', row['BibTeX']),
666
- row.get('Crossref_BibTeX_Protected', row['BibTeX']),
667
- row.get('Imported_Date', datetime.now().isoformat())
668
  ))
669
  except sqlite3.IntegrityError as e:
670
- print(f"⚠️ DB insert failed for {row['Key']}: {e}")
671
 
672
  conn.commit()
673
  db_id = session_id
@@ -681,19 +712,21 @@ def process_bibtex():
681
  else:
682
  final_bibtex_col = 'Crossref_BibTeX_LocalKey'
683
 
684
- response_df = df[[
685
- 'Key', 'Type', 'Authors', 'Title', 'Journal/Booktitle', 'Year',
686
- final_bibtex_col
687
- ]].copy()
 
688
 
689
- response_df.columns = [
690
- 'Key', 'Type', 'Authors', 'Title', 'Journal/Booktitle', 'Year', 'Final_BibTeX'
691
- ]
692
 
693
  return jsonify({
694
  'success': True,
695
  'count': len(df),
696
  'db_id': db_id,
 
 
697
  'data': response_df.to_dict(orient='records'),
698
  'full_data': df.to_dict(orient='records')
699
  })
@@ -703,170 +736,81 @@ def process_bibtex():
703
  traceback.print_exc()
704
  return jsonify({'error': str(e)}), 500
705
 
706
- @app.route('/api/process-latex', methods=['POST'])
707
- def process_latex():
708
- """Process LaTeX + BibTeX files (full overleaf.py pipeline)"""
709
- if not check_api_key():
710
- return jsonify({'error': 'Unauthorized'}), 401
711
-
712
  try:
713
- # Get files from form data
714
- if 'tex_file' not in request.files or 'bib_file' not in request.files:
715
- return jsonify({'error': 'Both tex_file and bib_file are required'}), 400
716
-
717
- tex_file = request.files['tex_file']
718
- bib_file = request.files['bib_file']
719
-
720
- if tex_file.filename == '' or bib_file.filename == '':
721
- return jsonify({'error': 'Both files must be provided'}), 400
722
-
723
- # Read file contents
724
- tex_content = tex_file.read().decode('utf-8')
725
- bib_content = bib_file.read().decode('utf-8')
726
-
727
- # Get options
728
- enrich = request.form.get('enrich', 'false').lower() == 'true'
729
- save_to_db = request.form.get('save_to_db', 'false').lower() == 'true'
730
-
731
- # Parse LaTeX citations
732
- citations_df = parse_citations_from_tex(tex_content)
733
-
734
- # Parse BibTeX
735
- bib_df = parse_bibtex_input(bib_content)
736
 
737
- # Merge
738
- merged_df = merge_citations_with_bib(citations_df, bib_df)
739
- merged_df.insert(0, "Index", range(1, len(merged_df) + 1))
740
 
741
- # Enrich if requested
742
- if enrich:
743
- merged_df = enrich_with_crossref(merged_df)
744
- else:
745
- merged_df['Crossref_BibTeX'] = merged_df['BibTeX']
746
- merged_df['Title_Similarity'] = 0
747
 
748
- # Add abbreviations and create all versions
749
- merged_df = add_journal_abbreviations(merged_df)
750
-
751
- # Save to database if requested
752
- db_id = None
753
- if save_to_db:
754
- conn = get_db_connection()
755
- cursor = conn.cursor()
756
- session_id = datetime.now().isoformat()
757
-
758
- for _, row in merged_df.iterrows():
759
- doi = row.get('DOI', '').strip()
760
- year_int = extract_year_int(row.get('Year', ''))
761
- key_val = row.get('Reference') or row.get('Key', f"ref_{row.get('Index', 0)}")
762
-
763
- try:
764
- cursor.execute('''
765
- INSERT OR REPLACE INTO bibliography
766
- (index_num, session_id, reference, frequency, sections, key, doi, type,
767
- authors, title, journal_booktitle, year, year_int, publisher, volume, pages,
768
- bibtex, crossref_bibtex, crossref_bibtex_localkey, title_similarity,
769
- journal_abbreviation, crossref_bibtex_abbrev, crossref_bibtex_protected,
770
- imported_date)
771
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
772
- ''', (
773
- row.get('Index'), session_id, row.get('Reference', ''),
774
- row.get('Frequency', 0), row.get('Sections', ''),
775
- key_val, doi, row.get('Type', ''), row.get('Authors', ''),
776
- row.get('Title', ''), row.get('Journal/Booktitle', ''),
777
- row.get('Year', ''), year_int, row.get('Publisher', ''),
778
- row.get('Volume', ''), row.get('Pages', ''),
779
- row.get('BibTeX', ''), row.get('Crossref_BibTeX', ''),
780
- row.get('Crossref_BibTeX_LocalKey', ''),
781
- row.get('Title_Similarity', 0), row.get('Journal_Abbreviation', ''),
782
- row.get('Crossref_BibTeX_Abbrev', ''),
783
- row.get('Crossref_BibTeX_Protected', ''),
784
- datetime.now().isoformat()
785
- ))
786
- except sqlite3.IntegrityError as e:
787
- print(f"⚠️ DB insert failed: {e}")
788
-
789
- conn.commit()
790
- db_id = session_id
791
- conn.close()
792
 
793
  return jsonify({
794
  'success': True,
795
- 'count': len(merged_df),
796
- 'db_id': db_id,
797
- 'citations_found': len(citations_df),
798
- 'data': merged_df.to_dict(orient='records')
799
  })
800
-
801
  except Exception as e:
802
- import traceback
803
- traceback.print_exc()
804
  return jsonify({'error': str(e)}), 500
805
 
806
- @app.route('/api/process-bib-only', methods=['POST'])
807
- def process_bib_only():
808
- """Process only BibTeX file (Mode 1 from overleaf.py)"""
809
- if not check_api_key():
810
- return jsonify({'error': 'Unauthorized'}), 401
 
 
811
 
812
  try:
813
- if 'bib_file' not in request.files:
814
- return jsonify({'error': 'bib_file is required'}), 400
815
-
816
- bib_file = request.files['bib_file']
817
-
818
- if bib_file.filename == '':
819
- return jsonify({'error': 'File must be provided'}), 400
820
-
821
- bib_content = bib_file.read().decode('utf-8')
822
- save_to_db = request.form.get('save_to_db', 'false').lower() == 'true'
823
 
824
- # Parse BibTeX
825
- df = parse_bibtex_input(bib_content)
 
 
 
 
826
 
827
- # Add "Used" column (empty by default)
828
- df["Used"] = None
829
 
830
- # Save to database if requested
831
- db_id = None
832
- if save_to_db:
833
- conn = get_db_connection()
834
- cursor = conn.cursor()
835
- session_id = datetime.now().isoformat()
836
-
837
- for _, row in df.iterrows():
838
- doi = row.get('DOI', '').strip()
839
- year_int = extract_year_int(row.get('Year', ''))
840
 
841
- try:
842
- cursor.execute('''
843
- INSERT OR REPLACE INTO bibliography
844
- (session_id, key, doi, type, authors, title, journal_booktitle,
845
- year, year_int, publisher, volume, pages, bibtex, used, imported_date)
846
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
847
- ''', (
848
- session_id, row['Key'], doi, row['Type'], row['Authors'],
849
- row['Title'], row['Journal/Booktitle'], row['Year'], year_int,
850
- row['Publisher'], row.get('Volume', ''), row.get('Pages', ''),
851
- row['BibTeX'], row.get('Used'), datetime.now().isoformat()
852
- ))
853
- except sqlite3.IntegrityError as e:
854
- print(f"⚠️ DB insert failed: {e}")
855
-
856
- conn.commit()
857
- db_id = session_id
858
- conn.close()
859
 
860
  return jsonify({
861
  'success': True,
862
- 'count': len(df),
863
- 'db_id': db_id,
864
- 'data': df.to_dict(orient='records')
865
  })
866
-
867
  except Exception as e:
868
- import traceback
869
- traceback.print_exc()
870
  return jsonify({'error': str(e)}), 500
871
 
872
  @app.route('/api/database/entries', methods=['GET'])
@@ -920,11 +864,9 @@ def clear_database():
920
  conn = get_db_connection()
921
  cursor = conn.cursor()
922
 
923
- # Delete all entries
924
  cursor.execute('DELETE FROM bibliography')
925
  deleted_count = cursor.rowcount
926
 
927
- # Reset the auto-increment counter
928
  cursor.execute('DELETE FROM sqlite_sequence WHERE name="bibliography"')
929
 
930
  conn.commit()
 
3
  Complete implementation matching overleaf.py functionality
4
  Includes LaTeX citation parsing, BibTeX processing, and full pipeline
5
 
6
+ FEATURES:
7
  - Fixed abbreviations to include periods (ISO 4 standard): "Energy" β†’ "Ener."
8
+ - LaTeX manuscript analysis for citation frequency and section tracking
9
+ - Filter references by section
10
+ - Clear entire database
11
  """
12
 
13
  from flask import Flask, render_template, request, jsonify, send_file
 
79
  # =====================================================================
80
 
81
  def parse_citations_from_tex(tex_content: str) -> pd.DataFrame:
82
+ """Parse citations from LaTeX content with section tracking"""
83
  print("πŸ“– Parsing citations from LaTeX")
84
 
85
  lines = tex_content.split('\n')
 
560
 
561
  @app.route('/api/process', methods=['POST'])
562
  def process_bibtex():
563
+ """Process BibTeX content with optional LaTeX analysis"""
564
  if not check_api_key():
565
  return jsonify({'error': 'Unauthorized'}), 401
566
 
567
  try:
568
+ # Handle both JSON and FormData
569
+ if request.is_json:
570
+ data = request.get_json()
571
+ bibtex_content = data.get('bibtex_content') or data.get('bibtex', '')
572
+ input_mode = data.get('input_mode', 'bibtex')
573
+ enrich = data.get('enrich', False)
574
+ abbreviate = data.get('abbreviate', False)
575
+ protect = data.get('protect', False)
576
+ save_to_db = data.get('save_to_db', False)
577
+ latex_file = None
578
+ else:
579
+ # FormData from file upload
580
+ bibtex_content = request.form.get('bibtex_content', '')
581
+ input_mode = request.form.get('input_mode', 'bibtex')
582
+ enrich = request.form.get('enrich', 'false').lower() == 'true'
583
+ abbreviate = request.form.get('abbreviate', 'false').lower() == 'true'
584
+ protect = request.form.get('protect', 'false').lower() == 'true'
585
+ save_to_db = request.form.get('save_to_db', 'false').lower() == 'true'
586
+ latex_file = request.files.get('latex_file')
587
 
588
  print(f"πŸ“₯ Received: {len(bibtex_content)} chars, mode={input_mode}")
589
 
 
600
  for i, title in enumerate(titles, 1):
601
  print(f" [{i}/{len(titles)}] Searching: {title[:50]}...")
602
  try:
 
603
  url = f"https://api.crossref.org/works?query.bibliographic={requests.utils.quote(title)}&rows=1"
604
  response = HTTP.get(url, timeout=15)
605
  items = response.json().get("message", {}).get("items", [])
606
 
607
  if items and items[0].get('DOI'):
608
  doi = items[0]['DOI']
 
609
  bibtex_r = HTTP.get(
610
  f"https://doi.org/{doi}",
611
  headers={"Accept": "application/x-bibtex"},
 
619
  except Exception as e:
620
  print(f" ⚠️ Failed: {e}")
621
 
 
622
  bibtex_content = '\n\n'.join(bibtex_entries)
623
  print(f"βœ… Retrieved {len(bibtex_entries)} BibTeX entries from titles")
624
 
625
  if not bibtex_content:
626
  return jsonify({'error': 'No BibTeX entries found for the provided titles'}), 400
627
 
 
628
  if not bibtex_content or len(bibtex_content.strip()) == 0:
629
  return jsonify({'error': 'No BibTeX content provided'}), 400
630
 
631
+ # Parse BibTeX
632
  df = parse_bibtex_input(bibtex_content)
633
 
634
  if df.empty:
635
  return jsonify({'error': 'No valid BibTeX entries found'}), 400
636
 
637
+ # Parse LaTeX file if provided
638
+ latex_analyzed = False
639
+ citations_found = 0
640
+ if latex_file:
641
+ print("πŸ“„ LaTeX file provided, analyzing...")
642
+ try:
643
+ latex_content = latex_file.read().decode('utf-8')
644
+ citations_df = parse_citations_from_tex(latex_content)
645
+ citations_found = len(citations_df)
646
+
647
+ # Merge LaTeX citation data with BibTeX data
648
+ df = merge_citations_with_bib(citations_df, df)
649
+ df.insert(0, "Index", range(1, len(df) + 1))
650
+ latex_analyzed = True
651
+ print(f"βœ… LaTeX analyzed: {citations_found} citations found")
652
+ except Exception as e:
653
+ print(f"⚠️ LaTeX analysis failed: {e}")
654
+
655
  # Enrich if requested
656
  if enrich:
657
  df = enrich_with_crossref(df)
 
672
  for _, row in df.iterrows():
673
  doi = row.get('DOI', '').strip()
674
  year_int = extract_year_int(row.get('Year', ''))
675
+ key_val = row.get('Reference') or row.get('Key', f"ref_{row.name}")
676
 
677
  try:
678
  cursor.execute('''
679
  INSERT OR REPLACE INTO bibliography
680
+ (index_num, session_id, reference, frequency, sections, key, doi, type,
681
+ authors, title, journal_booktitle, year, year_int, publisher, volume, pages,
682
+ bibtex, crossref_bibtex, crossref_bibtex_localkey, title_similarity,
683
+ journal_abbreviation, crossref_bibtex_abbrev, crossref_bibtex_protected,
684
+ imported_date)
685
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
686
  ''', (
687
+ row.get('Index'), session_id, row.get('Reference', key_val),
688
+ row.get('Frequency', 0), row.get('Sections', ''),
689
+ key_val, doi, row.get('Type', ''), row.get('Authors', ''),
690
+ row.get('Title', ''), row.get('Journal/Booktitle', ''),
691
+ row.get('Year', ''), year_int, row.get('Publisher', ''),
692
+ row.get('Volume', ''), row.get('Pages', ''),
693
+ row.get('BibTeX', ''), row.get('Crossref_BibTeX', ''),
694
+ row.get('Crossref_BibTeX_LocalKey', ''),
695
  row.get('Title_Similarity', 0), row.get('Journal_Abbreviation', ''),
696
+ row.get('Crossref_BibTeX_Abbrev', ''),
697
+ row.get('Crossref_BibTeX_Protected', ''),
698
+ datetime.now().isoformat()
699
  ))
700
  except sqlite3.IntegrityError as e:
701
+ print(f"⚠️ DB insert failed for {key_val}: {e}")
702
 
703
  conn.commit()
704
  db_id = session_id
 
712
  else:
713
  final_bibtex_col = 'Crossref_BibTeX_LocalKey'
714
 
715
+ # Prepare response columns
716
+ response_cols = ['Key', 'Type', 'Authors', 'Title', 'Journal/Booktitle', 'Year', final_bibtex_col]
717
+ if latex_analyzed:
718
+ response_cols.insert(6, 'Frequency')
719
+ response_cols.insert(7, 'Sections')
720
 
721
+ response_df = df[[col for col in response_cols if col in df.columns]].copy()
722
+ response_df.columns = list(response_df.columns[:-1]) + ['Final_BibTeX']
 
723
 
724
  return jsonify({
725
  'success': True,
726
  'count': len(df),
727
  'db_id': db_id,
728
+ 'latex_analyzed': latex_analyzed,
729
+ 'citations_found': citations_found,
730
  'data': response_df.to_dict(orient='records'),
731
  'full_data': df.to_dict(orient='records')
732
  })
 
736
  traceback.print_exc()
737
  return jsonify({'error': str(e)}), 500
738
 
739
+ @app.route('/api/sections/list', methods=['GET'])
740
+ def list_sections():
741
+ """Get list of all unique sections from database"""
 
 
 
742
  try:
743
+ conn = get_db_connection()
744
+ cursor = conn.cursor()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
745
 
746
+ cursor.execute('SELECT DISTINCT sections FROM bibliography WHERE sections IS NOT NULL AND sections != ""')
747
+ rows = cursor.fetchall()
748
+ conn.close()
749
 
750
+ # Parse and deduplicate sections
751
+ all_sections = set()
752
+ for row in rows:
753
+ if row[0]:
754
+ sections = [s.strip() for s in row[0].split(',')]
755
+ all_sections.update(sections)
756
 
757
+ sections_list = sorted(list(all_sections))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
 
759
  return jsonify({
760
  'success': True,
761
+ 'sections': sections_list
 
 
 
762
  })
 
763
  except Exception as e:
 
 
764
  return jsonify({'error': str(e)}), 500
765
 
766
+ @app.route('/api/sections/references', methods=['GET'])
767
+ def get_references_by_section():
768
+ """Get all references for a specific section"""
769
+ section = request.args.get('section', '')
770
+
771
+ if not section:
772
+ return jsonify({'error': 'Section parameter required'}), 400
773
 
774
  try:
775
+ conn = get_db_connection()
776
+ cursor = conn.cursor()
 
 
 
 
 
 
 
 
777
 
778
+ # Find all references that contain this section
779
+ cursor.execute('''
780
+ SELECT key, title, authors, year, frequency, sections, reference
781
+ FROM bibliography
782
+ WHERE sections LIKE ?
783
+ ''', (f'%{section}%',))
784
 
785
+ rows = cursor.fetchall()
786
+ conn.close()
787
 
788
+ references = []
789
+ for row in rows:
790
+ # Calculate frequency in this specific section
791
+ sections_list = [s.strip() for s in row[5].split(',') if s.strip()]
792
+ if section in sections_list:
793
+ # Count occurrences in this section (simplified - assumes equal distribution)
794
+ total_freq = row[4] or 0
795
+ num_sections = len(sections_list)
796
+ freq_in_section = total_freq // num_sections if num_sections > 0 else total_freq
 
797
 
798
+ references.append({
799
+ 'key': row[0],
800
+ 'title': row[1],
801
+ 'authors': row[2],
802
+ 'year': row[3],
803
+ 'frequency_in_section': freq_in_section,
804
+ 'total_frequency': total_freq,
805
+ 'all_sections': row[5]
806
+ })
 
 
 
 
 
 
 
 
 
807
 
808
  return jsonify({
809
  'success': True,
810
+ 'section': section,
811
+ 'references': references
 
812
  })
 
813
  except Exception as e:
 
 
814
  return jsonify({'error': str(e)}), 500
815
 
816
  @app.route('/api/database/entries', methods=['GET'])
 
864
  conn = get_db_connection()
865
  cursor = conn.cursor()
866
 
 
867
  cursor.execute('DELETE FROM bibliography')
868
  deleted_count = cursor.rowcount
869
 
 
870
  cursor.execute('DELETE FROM sqlite_sequence WHERE name="bibliography"')
871
 
872
  conn.commit()
templates/index.html CHANGED
@@ -251,6 +251,11 @@
251
  color: #0c5460;
252
  }
253
 
 
 
 
 
 
254
  .bibtex-preview {
255
  background: #f5f5f5;
256
  padding: 15px;
@@ -380,6 +385,56 @@
380
  background: #c82333;
381
  }
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  @media (max-width: 1024px) {
384
  .main-grid {
385
  grid-template-columns: 1fr;
@@ -404,6 +459,16 @@
404
  .hidden {
405
  display: none;
406
  }
 
 
 
 
 
 
 
 
 
 
407
  </style>
408
  </head>
409
  <body>
@@ -416,43 +481,14 @@
416
  <!-- Main Tabs -->
417
  <div class="card">
418
  <div class="tabs">
419
- <button class="tab-button active" onclick="switchMainTab('database')">πŸ’Ύ Database</button>
420
- <button class="tab-button" onclick="switchMainTab('process')">πŸ”„ Process References</button>
 
421
  <button class="tab-button" onclick="switchMainTab('help')">ℹ️ Help & Info</button>
422
  </div>
423
 
424
- <!-- Database Tab (Default) -->
425
- <div id="database" class="tab-content active">
426
- <h2>πŸ’Ύ Database Management</h2>
427
-
428
- <div class="stats-grid" id="statsContainer" style="margin-top: 20px;">
429
- <div class="stat-box">
430
- <h3 id="statTotal">0</h3>
431
- <p>Total Entries</p>
432
- </div>
433
- <div class="stat-box">
434
- <h3 id="statTypes">0</h3>
435
- <p>Entry Types</p>
436
- </div>
437
- <div class="stat-box">
438
- <h3 id="statYears">0</h3>
439
- <p>Unique Years</p>
440
- </div>
441
- </div>
442
-
443
- <div class="action-buttons">
444
- <button class="export-btn" onclick="loadDatabaseEntries()">πŸ”„ Refresh</button>
445
- <button class="export-btn" onclick="exportCSV()">πŸ“₯ Export CSV</button>
446
- <button class="export-btn" onclick="exportBibTeX()">πŸ“₯ Export BibTeX</button>
447
- <button class="export-btn" onclick="downloadDatabase()">πŸ’Ύ Download Database</button>
448
- <button class="clear-db-btn" onclick="clearDatabase()">πŸ—‘οΈ Clear Entire Database</button>
449
- </div>
450
-
451
- <div id="databaseEntries"></div>
452
- </div>
453
-
454
- <!-- Process References Tab -->
455
- <div id="process" class="tab-content">
456
  <div class="main-grid">
457
  <!-- Input Panel -->
458
  <div>
@@ -481,6 +517,15 @@
481
  year = {2024}
482
  }"></textarea>
483
 
 
 
 
 
 
 
 
 
 
484
  <div class="options">
485
  <h3 style="color: #333; margin-bottom: 15px; font-size: 1.1em;">Processing Options</h3>
486
 
@@ -551,6 +596,57 @@
551
  </div>
552
  </div>
553
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  <!-- Help Content -->
555
  <div id="help" class="tab-content">
556
  <h2>ℹ️ Help & Information</h2>
@@ -562,12 +658,36 @@
562
  <dd style="margin-left: 20px; color: #666;">Paste complete BibTeX entries. The system can enrich them with Crossref, apply abbreviations, and protect acronyms.</dd>
563
 
564
  <dt style="font-weight: 600; margin-top: 10px;">πŸ” Title Mode</dt>
565
- <dd style="margin-left: 20px; color: #666;">Paste only paper titles (one per line). The system automatically searches Crossref and retrieves complete BibTeX entries for each title. Perfect when you have a list of paper titles and need full references!</dd>
566
  </dl>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
 
568
- <h3 style="color: #333; margin: 20px 0 10px;">Journal Abbreviations (NEW!)</h3>
569
  <p style="margin-left: 20px; color: #666; line-height: 1.8;">
570
- The system now uses <strong>ISO 4 standard abbreviations with periods</strong>:
571
  </p>
572
  <ul style="margin-left: 40px; color: #666; line-height: 1.8;">
573
  <li>"Energy" β†’ "Ener."</li>
@@ -576,46 +696,20 @@
576
  <li>"IEEE Transactions on Neural Networks" β†’ "IEEE Trans. on Neural Netw."</li>
577
  </ul>
578
 
579
- <h3 style="color: #333; margin: 20px 0 10px;">How to Use</h3>
580
- <ol style="line-height: 1.8; margin-left: 20px;">
581
- <li><strong>Database Tab (Default):</strong> View and manage your saved references</li>
582
- <li><strong>Process Tab:</strong> Add new references
583
- <ul style="margin-left: 20px;">
584
- <li>Choose BibTeX Mode or Title Mode</li>
585
- <li>Paste your content</li>
586
- <li>Select processing options</li>
587
- <li>Click "Process References"</li>
588
- </ul>
589
- </li>
590
- <li><strong>Clear Database:</strong> Use the red "Clear Entire Database" button to remove all entries and start fresh</li>
591
- </ol>
592
-
593
- <h3 style="color: #333; margin: 20px 0 10px;">Processing Options Explained</h3>
594
  <dl style="margin-left: 20px;">
595
- <dt style="font-weight: 600; margin-top: 10px;">🌐 Enrich with Crossref</dt>
596
- <dd style="margin-left: 20px; color: #666;">Queries Crossref API to fetch updated metadata and BibTeX. Takes 3-5 seconds per entry.</dd>
597
 
598
- <dt style="font-weight: 600; margin-top: 10px;">πŸ“– Journal Abbreviations</dt>
599
- <dd style="margin-left: 20px; color: #666;">Replaces full journal names with ISO 4 standard abbreviations with periods.</dd>
600
 
601
- <dt style="font-weight: 600; margin-top: 10px;">πŸ›‘οΈ Protect Acronyms</dt>
602
- <dd style="margin-left: 20px; color: #666;">Wraps acronyms and multi-letter words in braces to preserve capitalization in LaTeX.</dd>
603
 
604
- <dt style="font-weight: 600; margin-top: 10px;">πŸ’Ύ Save to Database</dt>
605
- <dd style="margin-left: 20px; color: #666;">Stores all versions (original, enriched, abbreviated, protected) for future use.</dd>
606
  </dl>
607
-
608
- <h3 style="color: #333; margin: 20px 0 10px;">Data Storage</h3>
609
- <p style="margin-left: 20px; color: #666;">
610
- All data is stored in <code>refs_management.db</code> (SQLite). The database includes:
611
- </p>
612
- <ul style="margin-left: 20px;">
613
- <li>Original BibTeX</li>
614
- <li>Enriched metadata from Crossref</li>
615
- <li>Journal abbreviations with periods (ISO 4)</li>
616
- <li>Acronym-protected versions</li>
617
- <li>Timestamps and session info</li>
618
- </ul>
619
  </div>
620
  </div>
621
  </div>
@@ -623,16 +717,17 @@
623
 
624
  <script>
625
  let lastResults = null;
 
626
 
627
  function switchTab(tabName) {
628
- // Switch between Preview and BibTeX in results
629
  const parent = event.target.closest('.card') || document;
630
  parent.querySelectorAll('.tab-content').forEach(el => {
631
- if (el.id === 'database' || el.id === 'process' || el.id === 'help') return;
632
  el.classList.remove('active');
633
  });
634
  parent.querySelectorAll('.tab-button').forEach(el => {
635
- if (el.textContent.includes('Database') || el.textContent.includes('Process') || el.textContent.includes('Help')) return;
 
636
  el.classList.remove('active');
637
  });
638
 
@@ -641,7 +736,7 @@
641
  }
642
 
643
  function switchMainTab(tabName) {
644
- document.querySelectorAll('#database, #process, #help').forEach(el => {
645
  el.classList.remove('active');
646
  });
647
  document.querySelectorAll('.tabs > .tab-button').forEach(el => {
@@ -654,6 +749,8 @@
654
  if (tabName === 'database') {
655
  loadDatabaseEntries();
656
  loadStats();
 
 
657
  }
658
  }
659
 
@@ -690,6 +787,7 @@ Natural language processing techniques`;
690
 
691
  async function processReferences() {
692
  const bibtexContent = document.getElementById('bibtexInput').value;
 
693
  const inputMode = document.querySelector('input[name="inputMode"]:checked').value;
694
  const loading = document.getElementById('loadingIndicator');
695
  const message = document.getElementById('inputMessage');
@@ -704,19 +802,21 @@ Natural language processing techniques`;
704
  message.style.display = 'none';
705
 
706
  try {
 
 
 
 
 
 
 
 
 
 
 
 
707
  const response = await fetch('/api/process', {
708
  method: 'POST',
709
- headers: {
710
- 'Content-Type': 'application/json'
711
- },
712
- body: JSON.stringify({
713
- bibtex_content: bibtexContent,
714
- input_mode: inputMode,
715
- enrich: document.getElementById('enrichCheckbox').checked,
716
- abbreviate: document.getElementById('abbreviateCheckbox').checked,
717
- protect: document.getElementById('protectCheckbox').checked,
718
- save_to_db: document.getElementById('saveDbCheckbox').checked
719
- })
720
  });
721
 
722
  const data = await response.json();
@@ -725,9 +825,12 @@ Natural language processing techniques`;
725
  if (data.success) {
726
  lastResults = data;
727
  displayResults(data);
728
- showMessage(message,
729
- `βœ… Successfully processed ${data.count} references${data.db_id ? ' and saved to database' : ''}`,
730
- 'success');
 
 
 
731
 
732
  if (data.db_id) {
733
  loadDatabaseEntries();
@@ -754,6 +857,9 @@ Natural language processing techniques`;
754
 
755
  let tableHtml = '<table class="results-table"><thead><tr>';
756
  const columns = ['Key', 'Type', 'Authors', 'Title', 'Year'];
 
 
 
757
  columns.forEach(col => {
758
  tableHtml += `<th>${col}</th>`;
759
  });
@@ -767,11 +873,15 @@ Natural language processing techniques`;
767
  tableHtml += `<td>${row.Authors.substring(0, 50)}${row.Authors.length > 50 ? '...' : ''}</td>`;
768
  tableHtml += `<td>${row.Title.substring(0, 50)}${row.Title.length > 50 ? '...' : ''}</td>`;
769
  tableHtml += `<td>${row.Year}</td>`;
 
 
 
 
770
  tableHtml += '</tr>';
771
  });
772
 
773
  if (hasMore) {
774
- tableHtml += `<tr><td colspan="5" style="text-align: center; padding: 20px; color: #666; font-style: italic;">... and ${data.count - previewLimit} more references (check Database tab or export to see all)</td></tr>`;
775
  }
776
 
777
  tableHtml += '</tbody></table>';
@@ -801,7 +911,7 @@ Natural language processing techniques`;
801
  const container = document.getElementById('databaseEntries');
802
 
803
  if (data.count === 0) {
804
- container.innerHTML = '<p style="color: #999; text-align: center; padding: 40px;">No entries in database yet</p>';
805
  return;
806
  }
807
 
@@ -814,16 +924,20 @@ Natural language processing techniques`;
814
  }
815
 
816
  data.entries.slice(0, previewLimit).forEach(entry => {
 
 
 
817
  html += `
818
  <div class="db-entry-card">
819
  <h4>
820
- <span>${entry.key} <span class="badge badge-info">${entry.type}</span></span>
821
  <button class="delete-btn" onclick="deleteEntry('${entry.key}')">Delete</button>
822
  </h4>
823
  <div class="entry-meta">
824
  <span><strong>Title:</strong> ${entry.title.substring(0, 80)}${entry.title.length > 80 ? '...' : ''}</span>
825
  <span><strong>Year:</strong> ${entry.year || 'N/A'}</span>
826
  <span><strong>Added:</strong> ${new Date(entry.created_at).toLocaleDateString()}</span>
 
827
  </div>
828
  </div>
829
  `;
@@ -835,6 +949,74 @@ Natural language processing techniques`;
835
  }
836
  }
837
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838
  async function deleteEntry(key) {
839
  if (!confirm(`Delete entry "${key}"?`)) return;
840
 
@@ -857,7 +1039,6 @@ Natural language processing techniques`;
857
  return;
858
  }
859
 
860
- // Double confirmation
861
  if (!confirm('This action CANNOT be undone. All your references will be lost.\n\nClick OK to proceed with clearing the database.')) {
862
  return;
863
  }
@@ -873,6 +1054,7 @@ Natural language processing techniques`;
873
  alert(`βœ… ${result.message}`);
874
  loadDatabaseEntries();
875
  loadStats();
 
876
  } else {
877
  alert(`❌ Error: ${result.error}`);
878
  }
@@ -908,6 +1090,7 @@ Natural language processing techniques`;
908
 
909
  function clearInput() {
910
  document.getElementById('bibtexInput').value = '';
 
911
  }
912
 
913
  function showMessage(element, message, type) {
@@ -916,10 +1099,9 @@ Natural language processing techniques`;
916
  element.style.display = 'block';
917
  }
918
 
919
- // Initial load - Database tab is default
920
  window.addEventListener('DOMContentLoaded', function() {
921
  loadStats();
922
- loadDatabaseEntries();
923
  });
924
  </script>
925
  </body>
 
251
  color: #0c5460;
252
  }
253
 
254
+ .badge-warning {
255
+ background: #fff3cd;
256
+ color: #856404;
257
+ }
258
+
259
  .bibtex-preview {
260
  background: #f5f5f5;
261
  padding: 15px;
 
385
  background: #c82333;
386
  }
387
 
388
+ .file-upload {
389
+ margin: 15px 0;
390
+ padding: 15px;
391
+ background: #f5f5f5;
392
+ border-radius: 8px;
393
+ border: 2px dashed #ddd;
394
+ }
395
+
396
+ .file-upload label {
397
+ display: block;
398
+ margin-bottom: 8px;
399
+ font-weight: 600;
400
+ color: #333;
401
+ }
402
+
403
+ .file-upload input[type="file"] {
404
+ width: 100%;
405
+ padding: 8px;
406
+ border: 1px solid #ddd;
407
+ border-radius: 4px;
408
+ background: white;
409
+ }
410
+
411
+ .section-filter {
412
+ margin: 20px 0;
413
+ padding: 15px;
414
+ background: #f5f5f5;
415
+ border-radius: 8px;
416
+ }
417
+
418
+ .section-filter select {
419
+ width: 100%;
420
+ padding: 10px;
421
+ border: 2px solid #e0e0e0;
422
+ border-radius: 8px;
423
+ font-size: 1em;
424
+ background: white;
425
+ }
426
+
427
+ .empty-state {
428
+ text-align: center;
429
+ padding: 60px 20px;
430
+ color: #999;
431
+ }
432
+
433
+ .empty-state h3 {
434
+ color: #666;
435
+ margin-bottom: 10px;
436
+ }
437
+
438
  @media (max-width: 1024px) {
439
  .main-grid {
440
  grid-template-columns: 1fr;
 
459
  .hidden {
460
  display: none;
461
  }
462
+
463
+ .info-box {
464
+ background: #d1ecf1;
465
+ border: 1px solid #bee5eb;
466
+ color: #0c5460;
467
+ padding: 12px;
468
+ border-radius: 8px;
469
+ margin: 10px 0;
470
+ font-size: 0.9em;
471
+ }
472
  </style>
473
  </head>
474
  <body>
 
481
  <!-- Main Tabs -->
482
  <div class="card">
483
  <div class="tabs">
484
+ <button class="tab-button active" onclick="switchMainTab('process')">πŸ”„ Process References</button>
485
+ <button class="tab-button" onclick="switchMainTab('database')">πŸ’Ύ Database</button>
486
+ <button class="tab-button" onclick="switchMainTab('sections')">πŸ“‘ Select by Section</button>
487
  <button class="tab-button" onclick="switchMainTab('help')">ℹ️ Help & Info</button>
488
  </div>
489
 
490
+ <!-- Process References Tab (Default) -->
491
+ <div id="process" class="tab-content active">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  <div class="main-grid">
493
  <!-- Input Panel -->
494
  <div>
 
517
  year = {2024}
518
  }"></textarea>
519
 
520
+ <!-- Optional LaTeX File Upload -->
521
+ <div class="file-upload">
522
+ <label>πŸ“„ LaTeX Manuscript (Optional)</label>
523
+ <p style="color: #666; font-size: 0.9em; margin-bottom: 8px;">
524
+ Upload your .tex file to track citation frequency and sections
525
+ </p>
526
+ <input type="file" id="latexFile" accept=".tex" />
527
+ </div>
528
+
529
  <div class="options">
530
  <h3 style="color: #333; margin-bottom: 15px; font-size: 1.1em;">Processing Options</h3>
531
 
 
596
  </div>
597
  </div>
598
 
599
+ <!-- Database Tab -->
600
+ <div id="database" class="tab-content">
601
+ <h2>πŸ’Ύ Database Management</h2>
602
+
603
+ <div class="stats-grid" id="statsContainer" style="margin-top: 20px;">
604
+ <div class="stat-box">
605
+ <h3 id="statTotal">0</h3>
606
+ <p>Total Entries</p>
607
+ </div>
608
+ <div class="stat-box">
609
+ <h3 id="statTypes">0</h3>
610
+ <p>Entry Types</p>
611
+ </div>
612
+ <div class="stat-box">
613
+ <h3 id="statYears">0</h3>
614
+ <p>Unique Years</p>
615
+ </div>
616
+ </div>
617
+
618
+ <div class="action-buttons">
619
+ <button class="export-btn" onclick="loadDatabaseEntries()">πŸ”„ Refresh</button>
620
+ <button class="export-btn" onclick="exportCSV()">πŸ“₯ Export CSV</button>
621
+ <button class="export-btn" onclick="exportBibTeX()">πŸ“₯ Export BibTeX</button>
622
+ <button class="export-btn" onclick="downloadDatabase()">πŸ’Ύ Download Database</button>
623
+ <button class="clear-db-btn" onclick="clearDatabase()">πŸ—‘οΈ Clear Entire Database</button>
624
+ </div>
625
+
626
+ <div id="databaseEntries"></div>
627
+ </div>
628
+
629
+ <!-- Select by Section Tab -->
630
+ <div id="sections" class="tab-content">
631
+ <h2>πŸ“‘ Select References by Section</h2>
632
+
633
+ <div class="info-box">
634
+ <strong>ℹ️ Note:</strong> This feature requires uploading a LaTeX manuscript (.tex file) when processing references.
635
+ It shows which references appear in which sections and their citation frequency.
636
+ </div>
637
+
638
+ <div class="section-filter">
639
+ <label style="font-weight: 600; color: #333; margin-bottom: 8px; display: block;">
640
+ Filter by Section:
641
+ </label>
642
+ <select id="sectionSelect" onchange="filterBySection()">
643
+ <option value="">Loading sections...</option>
644
+ </select>
645
+ </div>
646
+
647
+ <div id="sectionResults"></div>
648
+ </div>
649
+
650
  <!-- Help Content -->
651
  <div id="help" class="tab-content">
652
  <h2>ℹ️ Help & Information</h2>
 
658
  <dd style="margin-left: 20px; color: #666;">Paste complete BibTeX entries. The system can enrich them with Crossref, apply abbreviations, and protect acronyms.</dd>
659
 
660
  <dt style="font-weight: 600; margin-top: 10px;">πŸ” Title Mode</dt>
661
+ <dd style="margin-left: 20px; color: #666;">Paste only paper titles (one per line). The system automatically searches Crossref and retrieves complete BibTeX entries for each title.</dd>
662
  </dl>
663
+
664
+ <h3 style="color: #333; margin: 20px 0 10px;">LaTeX Manuscript Analysis (NEW!)</h3>
665
+ <p style="margin-left: 20px; color: #666; line-height: 1.8;">
666
+ Upload your LaTeX manuscript (.tex file) to enable advanced tracking:
667
+ </p>
668
+ <ul style="margin-left: 40px; color: #666; line-height: 1.8;">
669
+ <li><strong>Citation Frequency:</strong> How many times each reference is cited</li>
670
+ <li><strong>Section Tracking:</strong> Which sections cite each reference</li>
671
+ <li><strong>Section Filtering:</strong> View references by specific section (Introduction, Methods, etc.)</li>
672
+ </ul>
673
+ <p style="margin-left: 20px; color: #666; line-height: 1.8; margin-top: 10px;">
674
+ Example LaTeX structure:
675
+ </p>
676
+ <pre style="margin-left: 40px; background: #f5f5f5; padding: 15px; border-radius: 5px; overflow-x: auto;">
677
+ \section{Introduction}
678
+ Text here \cite{reference_1} more text \cite{reference_2}
679
+ and again \cite{reference_1}
680
+
681
+ \section{Methods}
682
+ Description \cite{reference_3} and \cite{reference_1}
683
+ </pre>
684
+ <p style="margin-left: 20px; color: #666; line-height: 1.8;">
685
+ Results in: reference_1 appears in Introduction (2Γ—) and Methods (1Γ—)
686
+ </p>
687
 
688
+ <h3 style="color: #333; margin: 20px 0 10px;">Journal Abbreviations</h3>
689
  <p style="margin-left: 20px; color: #666; line-height: 1.8;">
690
+ The system uses <strong>ISO 4 standard abbreviations with periods</strong>:
691
  </p>
692
  <ul style="margin-left: 40px; color: #666; line-height: 1.8;">
693
  <li>"Energy" β†’ "Ener."</li>
 
696
  <li>"IEEE Transactions on Neural Networks" β†’ "IEEE Trans. on Neural Netw."</li>
697
  </ul>
698
 
699
+ <h3 style="color: #333; margin: 20px 0 10px;">Tab Overview</h3>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
700
  <dl style="margin-left: 20px;">
701
+ <dt style="font-weight: 600; margin-top: 10px;">πŸ”„ Process References (Tab 1)</dt>
702
+ <dd style="margin-left: 20px; color: #666;">Add new references via BibTeX or titles. Optionally upload LaTeX manuscript for section tracking.</dd>
703
 
704
+ <dt style="font-weight: 600; margin-top: 10px;">πŸ’Ύ Database (Tab 2)</dt>
705
+ <dd style="margin-left: 20px; color: #666;">View all saved references, export data, or clear database.</dd>
706
 
707
+ <dt style="font-weight: 600; margin-top: 10px;">πŸ“‘ Select by Section (Tab 3)</dt>
708
+ <dd style="margin-left: 20px; color: #666;">Filter references by LaTeX section. Only available if LaTeX manuscript was uploaded.</dd>
709
 
710
+ <dt style="font-weight: 600; margin-top: 10px;">ℹ️ Help & Info (Tab 4)</dt>
711
+ <dd style="margin-left: 20px; color: #666;">Documentation and usage guide.</dd>
712
  </dl>
 
 
 
 
 
 
 
 
 
 
 
 
713
  </div>
714
  </div>
715
  </div>
 
717
 
718
  <script>
719
  let lastResults = null;
720
+ let availableSections = [];
721
 
722
  function switchTab(tabName) {
 
723
  const parent = event.target.closest('.card') || document;
724
  parent.querySelectorAll('.tab-content').forEach(el => {
725
+ if (el.id === 'database' || el.id === 'process' || el.id === 'help' || el.id === 'sections') return;
726
  el.classList.remove('active');
727
  });
728
  parent.querySelectorAll('.tab-button').forEach(el => {
729
+ if (el.textContent.includes('Database') || el.textContent.includes('Process') ||
730
+ el.textContent.includes('Help') || el.textContent.includes('Section')) return;
731
  el.classList.remove('active');
732
  });
733
 
 
736
  }
737
 
738
  function switchMainTab(tabName) {
739
+ document.querySelectorAll('#database, #process, #help, #sections').forEach(el => {
740
  el.classList.remove('active');
741
  });
742
  document.querySelectorAll('.tabs > .tab-button').forEach(el => {
 
749
  if (tabName === 'database') {
750
  loadDatabaseEntries();
751
  loadStats();
752
+ } else if (tabName === 'sections') {
753
+ loadSections();
754
  }
755
  }
756
 
 
787
 
788
  async function processReferences() {
789
  const bibtexContent = document.getElementById('bibtexInput').value;
790
+ const latexFile = document.getElementById('latexFile').files[0];
791
  const inputMode = document.querySelector('input[name="inputMode"]:checked').value;
792
  const loading = document.getElementById('loadingIndicator');
793
  const message = document.getElementById('inputMessage');
 
802
  message.style.display = 'none';
803
 
804
  try {
805
+ const formData = new FormData();
806
+ formData.append('bibtex_content', bibtexContent);
807
+ formData.append('input_mode', inputMode);
808
+ formData.append('enrich', document.getElementById('enrichCheckbox').checked);
809
+ formData.append('abbreviate', document.getElementById('abbreviateCheckbox').checked);
810
+ formData.append('protect', document.getElementById('protectCheckbox').checked);
811
+ formData.append('save_to_db', document.getElementById('saveDbCheckbox').checked);
812
+
813
+ if (latexFile) {
814
+ formData.append('latex_file', latexFile);
815
+ }
816
+
817
  const response = await fetch('/api/process', {
818
  method: 'POST',
819
+ body: formData
 
 
 
 
 
 
 
 
 
 
820
  });
821
 
822
  const data = await response.json();
 
825
  if (data.success) {
826
  lastResults = data;
827
  displayResults(data);
828
+
829
+ let msg = `βœ… Successfully processed ${data.count} references`;
830
+ if (data.db_id) msg += ' and saved to database';
831
+ if (data.latex_analyzed) msg += ` (LaTeX analysis: ${data.citations_found} citations found)`;
832
+
833
+ showMessage(message, msg, 'success');
834
 
835
  if (data.db_id) {
836
  loadDatabaseEntries();
 
857
 
858
  let tableHtml = '<table class="results-table"><thead><tr>';
859
  const columns = ['Key', 'Type', 'Authors', 'Title', 'Year'];
860
+ if (data.latex_analyzed) {
861
+ columns.push('Frequency', 'Sections');
862
+ }
863
  columns.forEach(col => {
864
  tableHtml += `<th>${col}</th>`;
865
  });
 
873
  tableHtml += `<td>${row.Authors.substring(0, 50)}${row.Authors.length > 50 ? '...' : ''}</td>`;
874
  tableHtml += `<td>${row.Title.substring(0, 50)}${row.Title.length > 50 ? '...' : ''}</td>`;
875
  tableHtml += `<td>${row.Year}</td>`;
876
+ if (data.latex_analyzed) {
877
+ tableHtml += `<td><span class="badge badge-warning">${row.Frequency || 0}Γ—</span></td>`;
878
+ tableHtml += `<td style="font-size: 0.85em;">${row.Sections || '-'}</td>`;
879
+ }
880
  tableHtml += '</tr>';
881
  });
882
 
883
  if (hasMore) {
884
+ tableHtml += `<tr><td colspan="${columns.length}" style="text-align: center; padding: 20px; color: #666; font-style: italic;">... and ${data.count - previewLimit} more references (check Database tab or export to see all)</td></tr>`;
885
  }
886
 
887
  tableHtml += '</tbody></table>';
 
911
  const container = document.getElementById('databaseEntries');
912
 
913
  if (data.count === 0) {
914
+ container.innerHTML = '<div class="empty-state"><h3>No entries in database yet</h3><p>Process some references to get started</p></div>';
915
  return;
916
  }
917
 
 
924
  }
925
 
926
  data.entries.slice(0, previewLimit).forEach(entry => {
927
+ const freqBadge = entry.frequency ? `<span class="badge badge-warning">${entry.frequency}Γ— cited</span>` : '';
928
+ const sectionInfo = entry.sections ? `<br><strong>Sections:</strong> ${entry.sections}` : '';
929
+
930
  html += `
931
  <div class="db-entry-card">
932
  <h4>
933
+ <span>${entry.key} <span class="badge badge-info">${entry.type}</span> ${freqBadge}</span>
934
  <button class="delete-btn" onclick="deleteEntry('${entry.key}')">Delete</button>
935
  </h4>
936
  <div class="entry-meta">
937
  <span><strong>Title:</strong> ${entry.title.substring(0, 80)}${entry.title.length > 80 ? '...' : ''}</span>
938
  <span><strong>Year:</strong> ${entry.year || 'N/A'}</span>
939
  <span><strong>Added:</strong> ${new Date(entry.created_at).toLocaleDateString()}</span>
940
+ ${sectionInfo}
941
  </div>
942
  </div>
943
  `;
 
949
  }
950
  }
951
 
952
+ async function loadSections() {
953
+ try {
954
+ const response = await fetch('/api/sections/list');
955
+ const data = await response.json();
956
+
957
+ const select = document.getElementById('sectionSelect');
958
+ const resultsDiv = document.getElementById('sectionResults');
959
+
960
+ if (!data.success || data.sections.length === 0) {
961
+ select.innerHTML = '<option value="">No sections available</option>';
962
+ resultsDiv.innerHTML = '<div class="empty-state"><h3>No LaTeX Analysis Available</h3><p>Upload a LaTeX manuscript when processing references to enable this feature</p></div>';
963
+ return;
964
+ }
965
+
966
+ availableSections = data.sections;
967
+ select.innerHTML = '<option value="">-- Select a section --</option>';
968
+ data.sections.forEach(section => {
969
+ const option = document.createElement('option');
970
+ option.value = section;
971
+ option.textContent = section;
972
+ select.appendChild(option);
973
+ });
974
+
975
+ resultsDiv.innerHTML = '<p style="color: #666; text-align: center; padding: 40px;">Select a section to view its references</p>';
976
+ } catch (error) {
977
+ console.error('Error loading sections:', error);
978
+ }
979
+ }
980
+
981
+ async function filterBySection() {
982
+ const section = document.getElementById('sectionSelect').value;
983
+ const resultsDiv = document.getElementById('sectionResults');
984
+
985
+ if (!section) {
986
+ resultsDiv.innerHTML = '<p style="color: #666; text-align: center; padding: 40px;">Select a section to view its references</p>';
987
+ return;
988
+ }
989
+
990
+ try {
991
+ const response = await fetch(`/api/sections/references?section=${encodeURIComponent(section)}`);
992
+ const data = await response.json();
993
+
994
+ if (!data.success || data.references.length === 0) {
995
+ resultsDiv.innerHTML = `<div class="empty-state"><h3>No references found</h3><p>Section "${section}" has no citations</p></div>`;
996
+ return;
997
+ }
998
+
999
+ let html = `<h3 style="color: #333; margin-bottom: 15px;">References in "${section}" (${data.references.length} total)</h3>`;
1000
+ html += '<table class="results-table"><thead><tr><th>Key</th><th>Title</th><th>Authors</th><th>Year</th><th>Frequency</th></tr></thead><tbody>';
1001
+
1002
+ data.references.forEach(ref => {
1003
+ html += `<tr>
1004
+ <td><span class="badge badge-info">${ref.key}</span></td>
1005
+ <td>${ref.title.substring(0, 60)}${ref.title.length > 60 ? '...' : ''}</td>
1006
+ <td>${ref.authors.substring(0, 40)}${ref.authors.length > 40 ? '...' : ''}</td>
1007
+ <td>${ref.year}</td>
1008
+ <td><span class="badge badge-warning">${ref.frequency_in_section}Γ—</span></td>
1009
+ </tr>`;
1010
+ });
1011
+
1012
+ html += '</tbody></table>';
1013
+ resultsDiv.innerHTML = html;
1014
+ } catch (error) {
1015
+ console.error('Error filtering by section:', error);
1016
+ resultsDiv.innerHTML = '<div class="error">Error loading references for this section</div>';
1017
+ }
1018
+ }
1019
+
1020
  async function deleteEntry(key) {
1021
  if (!confirm(`Delete entry "${key}"?`)) return;
1022
 
 
1039
  return;
1040
  }
1041
 
 
1042
  if (!confirm('This action CANNOT be undone. All your references will be lost.\n\nClick OK to proceed with clearing the database.')) {
1043
  return;
1044
  }
 
1054
  alert(`βœ… ${result.message}`);
1055
  loadDatabaseEntries();
1056
  loadStats();
1057
+ loadSections();
1058
  } else {
1059
  alert(`❌ Error: ${result.error}`);
1060
  }
 
1090
 
1091
  function clearInput() {
1092
  document.getElementById('bibtexInput').value = '';
1093
+ document.getElementById('latexFile').value = '';
1094
  }
1095
 
1096
  function showMessage(element, message, type) {
 
1099
  element.style.display = 'block';
1100
  }
1101
 
1102
+ // Initial load
1103
  window.addEventListener('DOMContentLoaded', function() {
1104
  loadStats();
 
1105
  });
1106
  </script>
1107
  </body>