Commit Β·
f2bdec3
1
Parent(s): 3e10738
Update 2026-01-29 00:53:13
Browse files- app.py +46 -0
- templates/index.html +31 -7
app.py
CHANGED
|
@@ -88,6 +88,26 @@ def parse_bibtex_input(bibtex_content):
|
|
| 88 |
|
| 89 |
return pd.DataFrame(papers).drop_duplicates(subset="Key", keep="first").reset_index(drop=True)
|
| 90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
def protect_acronyms_in_fields(bibtex):
|
| 92 |
"""Protect acronyms with braces"""
|
| 93 |
def clean_field(field_name, text):
|
|
@@ -204,6 +224,15 @@ def add_journal_abbreviations(df):
|
|
| 204 |
row_data.get('Crossref_BibTeX_Abbrev', row['BibTeX'])
|
| 205 |
)
|
| 206 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
abbreviated_rows.append(row_data)
|
| 208 |
|
| 209 |
return pd.DataFrame(abbreviated_rows)
|
|
@@ -413,6 +442,23 @@ def export_database():
|
|
| 413 |
except Exception as e:
|
| 414 |
return jsonify({'error': str(e)}), 500
|
| 415 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 416 |
@app.route('/api/database/export-bibtex', methods=['GET'])
|
| 417 |
def export_bibtex():
|
| 418 |
"""Export database as BibTeX"""
|
|
|
|
| 88 |
|
| 89 |
return pd.DataFrame(papers).drop_duplicates(subset="Key", keep="first").reset_index(drop=True)
|
| 90 |
|
| 91 |
+
def clean_bibtex_fields(bibtex):
|
| 92 |
+
"""Remove unwanted fields from BibTeX entries"""
|
| 93 |
+
# Fields to remove
|
| 94 |
+
fields_to_remove = ['url', 'source', 'publication_stage', 'note']
|
| 95 |
+
|
| 96 |
+
for field in fields_to_remove:
|
| 97 |
+
# Remove field with its value (handles multi-line values)
|
| 98 |
+
bibtex = re.sub(
|
| 99 |
+
rf'\s*{field}\s*=\s*\{{[^}}]*\}},?\s*',
|
| 100 |
+
'\n ',
|
| 101 |
+
bibtex,
|
| 102 |
+
flags=re.IGNORECASE
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
# Clean up extra commas and whitespace
|
| 106 |
+
bibtex = re.sub(r',\s*,', ',', bibtex)
|
| 107 |
+
bibtex = re.sub(r',(\s*)\}', r'\1}', bibtex)
|
| 108 |
+
|
| 109 |
+
return bibtex
|
| 110 |
+
|
| 111 |
def protect_acronyms_in_fields(bibtex):
|
| 112 |
"""Protect acronyms with braces"""
|
| 113 |
def clean_field(field_name, text):
|
|
|
|
| 224 |
row_data.get('Crossref_BibTeX_Abbrev', row['BibTeX'])
|
| 225 |
)
|
| 226 |
|
| 227 |
+
# Clean unwanted fields from all BibTeX versions
|
| 228 |
+
row_data['BibTeX'] = clean_bibtex_fields(row_data['BibTeX'])
|
| 229 |
+
if row_data.get('Crossref_BibTeX'):
|
| 230 |
+
row_data['Crossref_BibTeX'] = clean_bibtex_fields(row_data['Crossref_BibTeX'])
|
| 231 |
+
if row_data.get('Crossref_BibTeX_Abbrev'):
|
| 232 |
+
row_data['Crossref_BibTeX_Abbrev'] = clean_bibtex_fields(row_data['Crossref_BibTeX_Abbrev'])
|
| 233 |
+
if row_data.get('Crossref_BibTeX_Protected'):
|
| 234 |
+
row_data['Crossref_BibTeX_Protected'] = clean_bibtex_fields(row_data['Crossref_BibTeX_Protected'])
|
| 235 |
+
|
| 236 |
abbreviated_rows.append(row_data)
|
| 237 |
|
| 238 |
return pd.DataFrame(abbreviated_rows)
|
|
|
|
| 442 |
except Exception as e:
|
| 443 |
return jsonify({'error': str(e)}), 500
|
| 444 |
|
| 445 |
+
@app.route('/api/database/download', methods=['GET'])
|
| 446 |
+
def download_database():
|
| 447 |
+
"""Download the SQLite database file"""
|
| 448 |
+
try:
|
| 449 |
+
db_path = app.config['DATABASE']
|
| 450 |
+
if not os.path.exists(db_path):
|
| 451 |
+
return jsonify({'error': 'Database file not found'}), 404
|
| 452 |
+
|
| 453 |
+
return send_file(
|
| 454 |
+
db_path,
|
| 455 |
+
mimetype='application/x-sqlite3',
|
| 456 |
+
as_attachment=True,
|
| 457 |
+
download_name='refs_management.db'
|
| 458 |
+
)
|
| 459 |
+
except Exception as e:
|
| 460 |
+
return jsonify({'error': str(e)}), 500
|
| 461 |
+
|
| 462 |
@app.route('/api/database/export-bibtex', methods=['GET'])
|
| 463 |
def export_bibtex():
|
| 464 |
"""Export database as BibTeX"""
|
templates/index.html
CHANGED
|
@@ -514,6 +514,7 @@
|
|
| 514 |
<button class="export-btn" onclick="loadDatabaseEntries()">π Refresh</button>
|
| 515 |
<button class="export-btn" onclick="exportCSV()">π₯ Export CSV</button>
|
| 516 |
<button class="export-btn" onclick="exportBibTeX()">π₯ Export BibTeX</button>
|
|
|
|
| 517 |
</div>
|
| 518 |
|
| 519 |
<div id="databaseEntries"></div>
|
|
@@ -660,7 +661,10 @@
|
|
| 660 |
const bibtexPreview = document.getElementById('bibtexPreview');
|
| 661 |
const jsonPreview = document.getElementById('jsonPreview');
|
| 662 |
|
| 663 |
-
|
|
|
|
|
|
|
|
|
|
| 664 |
|
| 665 |
// Table view
|
| 666 |
let tableHtml = '<table class="results-table"><thead><tr>';
|
|
@@ -670,7 +674,8 @@
|
|
| 670 |
});
|
| 671 |
tableHtml += '</tr></thead><tbody>';
|
| 672 |
|
| 673 |
-
data.data.
|
|
|
|
| 674 |
tableHtml += '<tr>';
|
| 675 |
tableHtml += `<td><span class="badge badge-info">${row.Key}</span></td>`;
|
| 676 |
tableHtml += `<td>${row.Type}</td>`;
|
|
@@ -680,21 +685,36 @@
|
|
| 680 |
tableHtml += '</tr>';
|
| 681 |
});
|
| 682 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 683 |
tableHtml += '</tbody></table>';
|
| 684 |
resultsTable.innerHTML = tableHtml;
|
| 685 |
|
| 686 |
-
// BibTeX view
|
| 687 |
let bibtexText = '';
|
| 688 |
-
data.full_data.
|
|
|
|
| 689 |
const bibField = document.getElementById('protectCheckbox').checked ?
|
| 690 |
'Crossref_BibTeX_Protected' :
|
| 691 |
'Crossref_BibTeX_Abbrev';
|
| 692 |
bibtexText += (row[bibField] || row.BibTeX) + '\n\n';
|
| 693 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 694 |
bibtexPreview.textContent = bibtexText;
|
| 695 |
|
| 696 |
-
// JSON view
|
| 697 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 698 |
}
|
| 699 |
|
| 700 |
async function loadDatabaseEntries() {
|
|
@@ -770,6 +790,10 @@
|
|
| 770 |
window.location.href = '/api/database/export-bibtex';
|
| 771 |
}
|
| 772 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 773 |
function clearInput() {
|
| 774 |
document.getElementById('bibtexInput').value = '';
|
| 775 |
}
|
|
@@ -784,4 +808,4 @@
|
|
| 784 |
loadStats();
|
| 785 |
</script>
|
| 786 |
</body>
|
| 787 |
-
</html>
|
|
|
|
| 514 |
<button class="export-btn" onclick="loadDatabaseEntries()">π Refresh</button>
|
| 515 |
<button class="export-btn" onclick="exportCSV()">π₯ Export CSV</button>
|
| 516 |
<button class="export-btn" onclick="exportBibTeX()">π₯ Export BibTeX</button>
|
| 517 |
+
<button class="export-btn" onclick="downloadDatabase()">πΎ Download Database</button>
|
| 518 |
</div>
|
| 519 |
|
| 520 |
<div id="databaseEntries"></div>
|
|
|
|
| 661 |
const bibtexPreview = document.getElementById('bibtexPreview');
|
| 662 |
const jsonPreview = document.getElementById('jsonPreview');
|
| 663 |
|
| 664 |
+
const previewLimit = 10;
|
| 665 |
+
const hasMore = data.count > previewLimit;
|
| 666 |
+
|
| 667 |
+
resultsInfo.innerHTML = `<strong>β
Processed ${data.count} references</strong>${hasMore ? ` <span style="color: #666;">(Showing first ${previewLimit} in preview)</span>` : ''}`;
|
| 668 |
|
| 669 |
// Table view
|
| 670 |
let tableHtml = '<table class="results-table"><thead><tr>';
|
|
|
|
| 674 |
});
|
| 675 |
tableHtml += '</tr></thead><tbody>';
|
| 676 |
|
| 677 |
+
const displayData = data.data.slice(0, previewLimit);
|
| 678 |
+
displayData.forEach(row => {
|
| 679 |
tableHtml += '<tr>';
|
| 680 |
tableHtml += `<td><span class="badge badge-info">${row.Key}</span></td>`;
|
| 681 |
tableHtml += `<td>${row.Type}</td>`;
|
|
|
|
| 685 |
tableHtml += '</tr>';
|
| 686 |
});
|
| 687 |
|
| 688 |
+
if (hasMore) {
|
| 689 |
+
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>`;
|
| 690 |
+
}
|
| 691 |
+
|
| 692 |
tableHtml += '</tbody></table>';
|
| 693 |
resultsTable.innerHTML = tableHtml;
|
| 694 |
|
| 695 |
+
// BibTeX view - limit to first few entries
|
| 696 |
let bibtexText = '';
|
| 697 |
+
const displayFullData = data.full_data.slice(0, previewLimit);
|
| 698 |
+
displayFullData.forEach(row => {
|
| 699 |
const bibField = document.getElementById('protectCheckbox').checked ?
|
| 700 |
'Crossref_BibTeX_Protected' :
|
| 701 |
'Crossref_BibTeX_Abbrev';
|
| 702 |
bibtexText += (row[bibField] || row.BibTeX) + '\n\n';
|
| 703 |
});
|
| 704 |
+
|
| 705 |
+
if (hasMore) {
|
| 706 |
+
bibtexText += `\n... and ${data.count - previewLimit} more references\n(Export BibTeX from Database tab to get all entries)`;
|
| 707 |
+
}
|
| 708 |
+
|
| 709 |
bibtexPreview.textContent = bibtexText;
|
| 710 |
|
| 711 |
+
// JSON view - limit preview
|
| 712 |
+
const jsonData = hasMore ? data.full_data.slice(0, previewLimit) : data.full_data;
|
| 713 |
+
let jsonText = JSON.stringify(jsonData, null, 2);
|
| 714 |
+
if (hasMore) {
|
| 715 |
+
jsonText += `\n\n// ... and ${data.count - previewLimit} more references`;
|
| 716 |
+
}
|
| 717 |
+
jsonPreview.textContent = jsonText;
|
| 718 |
}
|
| 719 |
|
| 720 |
async function loadDatabaseEntries() {
|
|
|
|
| 790 |
window.location.href = '/api/database/export-bibtex';
|
| 791 |
}
|
| 792 |
|
| 793 |
+
async function downloadDatabase() {
|
| 794 |
+
window.location.href = '/api/database/download';
|
| 795 |
+
}
|
| 796 |
+
|
| 797 |
function clearInput() {
|
| 798 |
document.getElementById('bibtexInput').value = '';
|
| 799 |
}
|
|
|
|
| 808 |
loadStats();
|
| 809 |
</script>
|
| 810 |
</body>
|
| 811 |
+
</html>
|