234 lines
7.8 KiB
Django/Jinja
234 lines
7.8 KiB
Django/Jinja
{% extends "base.html.jinja" %} {% block content %}
|
||
<h1>Welcome to SciPaperLoader</h1>
|
||
|
||
<div id="results-container"></div>
|
||
|
||
{% if success %}
|
||
<div class="alert alert-success mt-3">{{ success }}</div>
|
||
{% endif %} {% if error_message %}
|
||
<div class="alert alert-warning mt-3">
|
||
<h4>{{ error_message }}</h4>
|
||
<table class="table table-sm table-bordered">
|
||
<thead>
|
||
<tr>
|
||
<th>Row</th>
|
||
<th>DOI</th>
|
||
<th>Error</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for error in error_samples %}
|
||
<tr>
|
||
<td>{{ error.row }}</td>
|
||
<td>{{ error.doi }}</td>
|
||
<td>{{ error.error }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
<a href="{{ url_for('upload.download_error_log') }}" class="btn btn-outline-secondary">Download Full Error Log</a>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<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>
|
||
<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>
|
||
|
||
<script>
|
||
const form = document.getElementById("upload-form");
|
||
form.addEventListener("submit", function (e) {
|
||
e.preventDefault();
|
||
|
||
// Display loading state immediately
|
||
const progressModal = new bootstrap.Modal(document.getElementById("progressModal"));
|
||
progressModal.show();
|
||
const progressBar = document.getElementById("progressBar");
|
||
progressBar.style.width = "5%";
|
||
progressBar.textContent = "Starting...";
|
||
|
||
const formData = new FormData(form);
|
||
|
||
// Disable the form while processing
|
||
const submitButton = form.querySelector("button[type='submit']");
|
||
submitButton.disabled = true;
|
||
|
||
fetch(form.action, {
|
||
method: "POST",
|
||
body: formData,
|
||
})
|
||
.then((response) => response.json())
|
||
.then((data) => {
|
||
if (data.error) {
|
||
// Handle error
|
||
progressModal.hide();
|
||
alert(`Error: ${data.error}`);
|
||
submitButton.disabled = false;
|
||
return;
|
||
}
|
||
|
||
const taskId = data.task_id;
|
||
const interval = setInterval(() => {
|
||
fetch("{{ url_for('upload.task_status', task_id='') }}" + taskId)
|
||
.then((response) => response.json())
|
||
.then((status) => {
|
||
console.log("Task status:", status);
|
||
if (status.state === "SUCCESS") {
|
||
clearInterval(interval);
|
||
progressBar.style.width = "100%";
|
||
progressBar.textContent = "Completed!";
|
||
|
||
setTimeout(() => {
|
||
progressModal.hide();
|
||
showResults(status.result);
|
||
submitButton.disabled = false;
|
||
}, 1000);
|
||
} else if (status.state === "FAILURE") {
|
||
clearInterval(interval);
|
||
progressBar.style.width = "100%";
|
||
progressBar.classList.add("bg-danger");
|
||
progressBar.textContent = "Failed!";
|
||
|
||
setTimeout(() => {
|
||
progressModal.hide();
|
||
alert(`Task failed: ${status.error || "Unknown error"}`);
|
||
submitButton.disabled = false;
|
||
}, 1000);
|
||
} else {
|
||
// Update progress bar with more information
|
||
const progress = status.progress || 0;
|
||
progressBar.style.width = `${progress}%`;
|
||
progressBar.textContent = `${progress}% complete`;
|
||
document.getElementById("progressStatus").innerText = `Processing... (${status.state})`;
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
console.error("Failed to check task status:", err);
|
||
});
|
||
}, 1000);
|
||
})
|
||
.catch((err) => {
|
||
console.error("Upload failed:", err);
|
||
progressModal.hide();
|
||
alert("Upload failed. Please try again.");
|
||
submitButton.disabled = false;
|
||
});
|
||
});
|
||
|
||
const showResults = (result) => {
|
||
const message = `Upload completed! Added: ${result.added}, Updated: ${result.updated}, Skipped: ${result.skipped}, Errors: ${result.error_count}`;
|
||
|
||
let resultHTML = `<div class="alert alert-success">${message}</div>`;
|
||
|
||
// Add skipped records information
|
||
if (result.skipped > 0) {
|
||
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) {
|
||
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>`;
|
||
}
|
||
|
||
document.getElementById("results-container").innerHTML = resultHTML;
|
||
};
|
||
</script>
|
||
{% endblock content %} |