Skip to content

Dataset download zip email #129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

klpoland
Copy link
Collaborator

@klpoland klpoland commented Jun 19, 2025

SFDS-186

  • Added celery worker and mailhog to docker compose
  • refactored celery configs
  • Use smtp email backend on local
  • Added get_dataset_files and download_file helper functions to avoid duplicate code, easier maintenance
  • Added celery task for zipping and emailing files form dataset as well as some test tasks
  • Added tests for celery, file download, command to run celery tests with easy to read logs
  • Added download button, JS to run DatasetDownloadView
  • make test, make test-integration, and make pre-commit pass locally

@klpoland klpoland requested a review from lucaspar June 19, 2025 17:06
@klpoland klpoland self-assigned this Jun 19, 2025
@klpoland klpoland added feature New feature or request gateway Gateway component styling Special focus on styling of front-end components javascript Pull requests that update javascript code labels Jun 19, 2025
to=[user_email],
)

email.attach(zip_filename, zip_buffer.getvalue(), "application/zip")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's provide a temporary download link instead, this can be GBs of data.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's what I do on REDI-NET for several things. So maybe the process is actually save the zip file on the server so they can download it? what does temporary mean? As in, the link only exists for a period of time and disappears when they use it? should the zip file be cleaned out once it's downloaded or remain on the server?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the link only exists for a period of time and disappears when they use it?

we can allow multiple uses (e.g. download failure), but let's make it expire in e.g. 24h and clean up the zip.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's also a case to be made about not recreating the zip if it already exists for a given dataset, but this could be a future PR. For example, multiple "download" clicks while the link is still valid.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2025-06-20 at 8 06 01 AM

Comment on lines 133 to 211
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM loaded, looking for download buttons...');

// Handle download button clicks
document.querySelectorAll('.download-dataset-btn').forEach(function(button) {
console.log('Found download button:', button);
button.addEventListener('click', function() {
const datasetUuid = this.getAttribute('data-dataset-uuid');
const datasetName = this.getAttribute('data-dataset-name');

console.log('Download button clicked for dataset:', datasetUuid, datasetName);

// Disable button and show loading state
const originalText = this.innerHTML;
this.innerHTML = '<i class="bi bi-hourglass-split"></i> Processing...';
this.disabled = true;

// Make API request
console.log('Making request to:', `/users/dataset-download/${datasetUuid}/`);
fetch(`/users/dataset-download/${datasetUuid}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
})
.then(response => {
console.log('Response status:', response.status);
if (response.ok) {
return response.json().then(data => {
console.log('Success response:', data);
// Show success message
showAlert('success', `Download request sent for "${datasetName}". You will receive an email with the files shortly.`);
this.innerHTML = '<i class="bi bi-check-circle"></i> Sent!';
this.classList.remove('btn-success');
this.classList.add('btn-secondary');
});
} else {
return response.json().then(data => {
console.log('Error response:', data);
// Show error message
const errorMessage = data.detail || data.message || 'Failed to send download request';
showAlert('danger', `Error: ${errorMessage}`);
this.innerHTML = originalText;
this.disabled = false;
});
}
})
.catch(error => {
console.error('Network error:', error);
showAlert('danger', 'Network error. Please try again.');
this.innerHTML = originalText;
this.disabled = false;
});
});
});

// Function to show alerts
function showAlert(type, message) {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
`;

// Insert at the top of the content area
const contentArea = document.querySelector('.main-content-area');
contentArea.insertBefore(alertDiv, contentArea.firstChild);

// Auto-dismiss after 5 seconds
setTimeout(() => {
if (alertDiv.parentNode) {
alertDiv.remove();
}
}, 5000);
}
});
</script>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove most (all?) console.log

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a usability improvement here, since downloads are async.

We should probably communicate these things to the user before starting the task, not verbatim:

  1. Expect an email with the download link sent to <email-address>;
  2. Ask for confirmation to proceed with download.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2025-06-20 at 8 05 32 AM

@klpoland
Copy link
Collaborator Author

klpoland commented Jun 20, 2025

  • Added an extra confirmation screen
  • Send user to a download page link (check login and ownership of dataset to make sure correct person is downloading)
  • Added celery flower to compose
  • Added temporary zip file model to track expiry

I'm doing soft deletion of the DB object but delete the actual files, this is a management command, maybe run this at some regular interval, probably not super often. I'm open to suggestions on how to handle the zip files when they expire.

@lucaspar
Copy link
Member

lucaspar commented Jun 20, 2025

maybe run this at some regular interval, probably not super often. I'm open to suggestions on how to handle the zip files when they expire.

maybe running a celery's periodic task once or twice a day?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request gateway Gateway component javascript Pull requests that update javascript code styling Special focus on styling of front-end components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants