175 lines
6.0 KiB
Django/Jinja
175 lines
6.0 KiB
Django/Jinja
{% extends "base.html.jinja" %}
|
||
|
||
{% block title %}Import CSV{% endblock title %}
|
||
|
||
{% block content %}
|
||
<h1>Welcome to SciPaperLoader</h1>
|
||
|
||
<!-- Include flash messages template -->
|
||
{% include "partials/flash_messages.html.jinja" %}
|
||
|
||
<div id="results-container"></div>
|
||
|
||
<div class="alert alert-info">
|
||
<p>
|
||
<strong>Instructions:</strong> Please upload a CSV file containing academic
|
||
paper metadata. The file must include the following columns:
|
||
</p>
|
||
<ul>
|
||
<li><code>alternative_id</code> – an alternative title or abbreviation</li>
|
||
<li><code>journal</code> – the journal name</li>
|
||
<li><code>doi</code> – the digital object identifier</li>
|
||
<li><code>issn</code> – the ISSN of the journal</li>
|
||
<li><code>title</code> – the title of the paper</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<form method="post" action="{{ url_for('upload.upload') }}" enctype="multipart/form-data" id="upload-form">
|
||
<div class="form-group">
|
||
<label for="file">Upload CSV File</label>
|
||
<input type="file" name="file" id="file" class="form-control" required />
|
||
</div>
|
||
<div class="form-group mt-3">
|
||
<label for="delimiter">Choose CSV Delimiter</label>
|
||
<select name="delimiter" id="delimiter" class="form-control">
|
||
<option value=",">Comma (,)</option>
|
||
<option value=";">Semicolon (;)</option>
|
||
<option value="\t">Tab (\\t)</option>
|
||
<option value="|">Pipe (|)</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group mt-3">
|
||
<label for="duplicate_strategy">Duplicate Handling Strategy</label>
|
||
<select name="duplicate_strategy" id="duplicate_strategy" class="form-control">
|
||
{% for strategy_id, strategy in duplicate_strategies.items() %}
|
||
<option value="{{ strategy_id }}" {% if strategy.is_default %}selected{% endif %}>
|
||
{{ strategy.name }} - {{ strategy.description }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<button type="submit" class="btn btn-primary mt-3">Upload</button>
|
||
</form>
|
||
|
||
<!-- Progress Modal -->
|
||
<div id="progressModal" class="modal fade" tabindex="-1">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">Processing Your Upload</h5>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="progress">
|
||
<div id="progressBar" class="progress-bar" role="progressbar">0%</div>
|
||
</div>
|
||
<p id="progressStatus" class="mt-2 text-center">Starting...</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{% endblock content %}
|
||
|
||
{% block scripts %}
|
||
{{ super() }}
|
||
<!-- Configuration data in JSON format for clean separation -->
|
||
<script type="application/json" id="upload-config">
|
||
{
|
||
"statusUrlTemplate": {{ (url_for('upload.task_status', task_id='') ~ '{taskId}')|tojson }}
|
||
}
|
||
</script>
|
||
|
||
<script src="{{ url_for('static', filename='js/form-handler.js') }}"></script>
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
// Read configuration from JSON
|
||
const config = JSON.parse(document.getElementById('upload-config').textContent);
|
||
|
||
// Initialize form handler with custom callbacks
|
||
const uploadFormHandler = new FormHandler('upload-form', {
|
||
statusUrlTemplate: config.statusUrlTemplate,
|
||
onSuccess: showResults,
|
||
onError: (error) => showFlashMessage(`Upload failed: ${error}`, 'error')
|
||
});
|
||
});
|
||
|
||
const showResults = (result) => {
|
||
// Show main success message as overlay
|
||
const message = `Upload completed! Added: ${result.added}, Updated: ${result.updated}, Skipped: ${result.skipped}, Errors: ${result.error_count}`;
|
||
showFlashMessage(message, 'success');
|
||
|
||
// Build detailed results HTML for the results container
|
||
let resultHTML = '';
|
||
|
||
// Add skipped records information
|
||
if (result.skipped > 0) {
|
||
showFlashMessage(`${result.skipped} records were skipped`, 'info');
|
||
resultHTML += `
|
||
<div class="alert alert-info">
|
||
<h4>${result.skipped} records were skipped</h4>
|
||
<p>${result.skipped_reason_summary || "Records were skipped because they already exist in the database."}</p>
|
||
${result.skipped_records && result.skipped_records.length > 0 ? `
|
||
<p>Examples of skipped records:</p>
|
||
<table class="table table-sm table-bordered">
|
||
<thead>
|
||
<tr>
|
||
<th>Row</th>
|
||
<th>DOI</th>
|
||
<th>Reason</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
${result.skipped_records.map(record => `
|
||
<tr>
|
||
<td>${record.row}</td>
|
||
<td>${record.doi}</td>
|
||
<td>${record.reason}</td>
|
||
</tr>
|
||
`).join('')}
|
||
</tbody>
|
||
</table>
|
||
` : ''}
|
||
</div>`;
|
||
}
|
||
|
||
// Existing error display code
|
||
if (result.error_count > 0) {
|
||
showFlashMessage(`${result.error_count} errors occurred during upload`, 'warning');
|
||
resultHTML += `
|
||
<div class="alert alert-warning">
|
||
<h4>Some errors occurred (${result.error_count} total)</h4>
|
||
<p>Showing first ${result.errors.length} of ${result.error_count} errors:</p>
|
||
<table class="table table-sm table-bordered">
|
||
<thead>
|
||
<tr>
|
||
<th>Row</th>
|
||
<th>DOI</th>
|
||
<th>Error</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>`;
|
||
|
||
result.errors.forEach(error => {
|
||
resultHTML += `
|
||
<tr>
|
||
<td>${error.row}</td>
|
||
<td>${error.doi}</td>
|
||
<td>${error.error}</td>
|
||
</tr>`;
|
||
});
|
||
|
||
resultHTML += `
|
||
</tbody>
|
||
</table>
|
||
<p class="mt-2">Download the complete error log with all ${result.error_count} errors:</p>
|
||
<a href="/upload/download_error_log/${result.task_id}" class="btn btn-outline-secondary">
|
||
Download Full Error Log
|
||
</a>
|
||
</div>`;
|
||
}
|
||
|
||
// Display detailed results in container
|
||
document.getElementById("results-container").innerHTML = resultHTML;
|
||
};
|
||
</script>
|
||
{% endblock scripts %} |