Skip to content

Commit

Permalink
Merge branch 'ui-v2' of https://github.com/IGS/gEAR into ui-v2
Browse files Browse the repository at this point in the history
  • Loading branch information
adkinsrs committed Sep 6, 2024
2 parents 937cd02 + 898c31b commit 96762a7
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 11 deletions.
10 changes: 6 additions & 4 deletions lib/gear/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def __init__(self, metadata=None, file_path=None):


def read_file(self, file_path=None):
print(f'DEBUG: Reading metadata file: {file_path}', file=sys.stderr)
"""
Reads dataset_metadata.xlsx or dataset_metadata.json into a pandas dataframe
Expand All @@ -95,7 +96,8 @@ def read_file(self, file_path=None):
json_data = {'field': [], 'value': []}

with open(file_path) as json_file:
data = ast.literal_eval(json_file.read())
data = json.loads(json_file.read())

for d in data:
json_data['field'].append(d)
json_data['value'].append(data[d])
Expand Down Expand Up @@ -273,14 +275,14 @@ def save_to_mysql(self, status=None):
is_public = 0
All datasets will save as private. Once the upload is complete,
the user can change the dataset to public on the dataset manager.
load_status = 'pending'
All datasets will save as 'pending'.
load_status = 'complete'
All datasets will save as 'complete'.
"""
if self.metadata is None:
raise Exception("No values to evaluate. Please load a metadata file first.")

if status is None:
status = 'pending'
status = 'complete'

df = self.metadata

Expand Down
117 changes: 117 additions & 0 deletions www/cgi/finalize_uploaded_expression_dataset.cgi
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/opt/bin/python3

"""
At this point we should have a directory with a JSON file with metadata, a tarball
uploaded by the user, and a status.json file.
This script does the following:
- Loads the JSON metadata file and stores it in MySQL
- Migrates the H5AD file to the proper directory
- Migrates the original tarball so it can be downloaded by users
Returns a status of these steps as JSON data like this:
result = {
"success": 1,
"metadata_loaded": 1,
"h5ad_migrated": 1,
"tarball_migrated": 1,
"message": "All steps completed successfully."
}
"""

import cgi
import json
import os, sys

lib_path = os.path.abspath(os.path.join('..', '..', 'lib'))
sys.path.append(lib_path)
import geardb

from gear.metadata import Metadata

user_upload_file_base = '../uploads/files'
dataset_final_dir = '../datasets'

result = {
"success": 0,
"metadata_loaded": 0,
"h5ad_migrated": 0,
"tarball_migrated": 0,
"message": ""
}

def main():
print('Content-Type: application/json\n\n', flush=True)

form = cgi.FieldStorage()
share_uid = form.getvalue('share_uid')
session_id = form.getvalue('session_id')
dataset_id = form.getvalue('dataset_uid')

user = geardb.get_user_from_session_id(session_id)
if user is None:
result['message'] = 'User ID not found. Please log in to continue.'
print(json.dumps(result))
sys.exit(0)

dataset_upload_dir = os.path.join(user_upload_file_base, session_id, share_uid)

# if the upload directory doesn't exist, we can't process the dataset
if not os.path.exists(dataset_upload_dir):
result['message'] = 'Dataset/directory not found.'
print(json.dumps(result))
sys.exit(0)

# Load the metadata
metadata_file = os.path.join(dataset_upload_dir, 'metadata.json')
if not os.path.exists(metadata_file):
result['message'] = 'Metadata file not found.'
print(json.dumps(result))
sys.exit(0)

with open(metadata_file, 'r') as f:
metadata = json.load(f)

# Load the metadata into the database
metadata = Metadata(file_path=metadata_file)
try:
metadata.save_to_mysql(status='complete')
result['metadata_loaded'] = 1
except Exception as e:
result['message'] = 'Error saving metadata to MySQL: {}'.format(str(e))
print(json.dumps(result))
sys.exit(0)

# migrate the H5AD file
h5ad_file = os.path.join(dataset_upload_dir, f'{share_uid}.h5ad')
if not os.path.exists(h5ad_file):
result['message'] = 'H5AD file not found: {}'.format(h5ad_file)
print(json.dumps(result))
sys.exit(0)

h5ad_dest = os.path.join(dataset_final_dir, f'{dataset_id}.h5ad')

try:
os.rename(h5ad_file, h5ad_dest)
result['h5ad_migrated'] = 1
except Exception as e:
result['message'] = 'Error migrating H5AD file: {}'.format(str(e))
print(json.dumps(result))
sys.exit(0)


# if we made it this far, all is well, so return success
result['success'] = 1
result['message'] = 'All steps completed successfully.'
print(json.dumps(result))






if __name__ == '__main__':
main()
5 changes: 4 additions & 1 deletion www/cgi/get_uploads_in_progress.cgi
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def main():

result['uploads'].append( {
'share_id': share_id,
'dataset_id': metadata.get('dataset_uid', ''),
'dataset_type': metadata.get('dataset_type', ''),
'title': metadata.get('title', ''),
'status': 'metadata uploaded',
Expand All @@ -81,7 +82,9 @@ def main():
if processing_status == 'processing':
result['uploads'][-1]['status'] = 'processing'
result['uploads'][-1]['load_step'] = 'process-dataset'

elif processing_status == 'complete':
result['uploads'][-1]['status'] = 'processed'
result['uploads'][-1]['load_step'] = 'finalize-dataset'

result['success'] = 1
print(json.dumps(result))
Expand Down
8 changes: 6 additions & 2 deletions www/js/common.v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,12 @@ const apiCallsMixin = {
const {data} = await axios.post("/cgi/get_user_history_entries.cgi", convertToFormData(payload));
return data;
},

async finalizeExpressionUpload(formData) {
const payload = new URLSearchParams(formData);
const {data} = await axios.post("/cgi/finalize_uploaded_expression_dataset.cgi", payload);
return data;
},
/**
* Retrieves session information.
* @returns {Promise<Object>} The session information.
Expand All @@ -1301,7 +1307,6 @@ const apiCallsMixin = {
const {data} = await axios.post("/cgi/login.v2.cgi", payload);
return data;
},

/**
* Parses the metadata file using the provided form data.
* @param {FormData} formData - The form data containing the metadata file.
Expand All @@ -1312,7 +1317,6 @@ const apiCallsMixin = {
const {data} = await axios.post("/cgi/upload_expression_metadata.cgi", formData);
return data;
},

/**
* Renames a dataset collection.
*
Expand Down
56 changes: 56 additions & 0 deletions www/js/upload_dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ window.onload=function() {
}
});

document.getElementById('dataset-finalize-submit').addEventListener('click', (event) => {
event.preventDefault();
document.getElementById('dataset-finalize-submit').disabled = true;
document.getElementById('finalize-dataset-status-c').classList.remove('is-hidden');

finalizeUpload();
});

document.getElementById('dataset-finalize-next-step').addEventListener('click', (event) => {
event.preventDefault();

// Move to the next step
stepTo('curate-dataset');
});

document.getElementById('metadata-file-input').addEventListener('change', (event) => {
// Was a file selected?
if (event.target.files.length > 0) {
Expand Down Expand Up @@ -174,6 +189,42 @@ const checkDatasetProcessingStatus = async () => {
}
}

const finalizeUpload = async () => {
let formData = new FormData();
formData.append('share_uid', share_uid);
formData.append('session_id', CURRENT_USER.session_id);
formData.append('dataset_uid', dataset_uid);

const data = await apiCallsMixin.finalizeExpressionUpload(formData);

if (data['metadata_loaded']) {
document.getElementById('finalize-storing-metadata').classList.remove('mdi-checkbox-blank-outline');
document.getElementById('finalize-storing-metadata').classList.add('mdi-checkbox-marked');
} else {
document.getElementById('finalize-storing-metadata').classList.remove('mdi-checkbox-blank-outline');
document.getElementById('finalize-storing-metadata').classList.add('mdi-skull-scan');
}

if (data['h5ad_migrated']) {
document.getElementById('finalize-migrating-h5ad').classList.remove('mdi-checkbox-blank-outline');
document.getElementById('finalize-migrating-h5ad').classList.add('mdi-checkbox-marked');
} else {
document.getElementById('finalize-migrating-h5ad').classList.remove('mdi-checkbox-blank-outline');
document.getElementById('finalize-migrating-h5ad').classList.add('mdi-skull-scan');
}

if (data.success) {
console.log("SUCCESS");
console.log(data);
document.getElementById('dataset-finalize-next-step').disabled = false;
} else {
console.log("ERROR");
console.log(data);
document.getElementById('dataset-finalize-status-message').innerText = data.message;
document.getElementById('dataset-finalize-status-message-c').classList.remove('is-hidden');
}
}

const populateMetadataFormFromFile = async () => {
const formData = new FormData(document.getElementById('metadata-upload-form'));
const data = await apiCallsMixin.parseMetadataFile(formData);
Expand Down Expand Up @@ -322,6 +373,7 @@ const loadUploadsInProgress = async () => {
data.uploads.forEach((upload) => {
let clone = template.content.cloneNode(true);
clone.querySelector('tr').dataset.shareId = upload.share_id;
clone.querySelector('tr').dataset.datasetId = upload.dataset_id;
clone.querySelector('tr').dataset.loadStep = upload.load_step;

clone.querySelector('.submission-share-id').textContent = upload.share_id;
Expand All @@ -336,6 +388,10 @@ const loadUploadsInProgress = async () => {
row.addEventListener('click', (event) => {
share_uid = row.dataset.shareId;
const step = row.dataset.loadStep;

if (row.dataset.datasetId) {
dataset_uid = row.dataset.datasetId;
}

// Do we want to dynamically load the next step or page refresh for it?
// If dynamic we have to reset all the forms.
Expand Down
97 changes: 93 additions & 4 deletions www/upload_dataset.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ <h1>Submissions in progress</h1>
<tbody id="submissions-in-progress-table-tbody"></tbody>
</table>
<template id="submission-history-row">
<tr class="submission-history-row" data-share-id="" data-load-step="">
<tr class="submission-history-row" data-share-id="" data-dataset-id="" data-load-step="">
<td class="submission-share-id"></td>
<td class="submission-status"></td>
<td class="submission-dataset-type"></td>
Expand Down Expand Up @@ -790,10 +790,99 @@ <h3>Message: <span id="step-process-dataset-status-message"></span></h3>
</div>
</div>
</div>
<div id="step-finalize-dataset-c" class="step-c is-hidden">
<h1>Step - Finalize submission</h1>
<p>
Your dataset has been processed and is ready to be submitted.
</p>
<hr />
<div class="columns">
<div class="column">
<!-- set whether dataset is public or private -->
<div class="field">
<label class="label">
Dataset visibility
<span class="icon has-tooltip-right has-tooltip-arrow"
data-tooltip="Choose whether this dataset is public or private. Public datasets are visible to all users.">
<i class="mdi mdi-information"></i>
</span>
</label>
<div class="control">
<label class="radio">
<input type="radio" name="dataset-visibility" value="public">
Public
</label>
<label class="radio">
<input type="radio" name="dataset-visibility" value="private" checked>
Private (can be changed later)
</label>
</div>
</div>
<div class="field">
<div class="control mt-4">
<button class="button is-primary" id="dataset-finalize-submit">Finalize submission</button>
</div>
</div>
<div id="dataset-finalize-status-message-c" class="is-hidden">
<p>
Please use the Feedback link on the left and provide this error message:
</p>
<span class="icon">
<i class="mdi mdi-information"></i>
</span>
<span id="dataset-finalize-status-message"></span>

</div>
</div>
<div class="column">
<div id="finalize-dataset-status-c" class="is-hidden">
<h3>Steps being performed:</h3>
<ul id="finalize-dataset-status-list">
<li>
<span class="icon-text">
<span class="icon">
<i id="finalize-storing-metadata" class="mdi mdi-checkbox-blank-outline"></i>
</span>
<span>Storing metadata</span>
</span>
</li>
</li>
<li>
<span class="icon-text">
<span class="icon">
<i id="finalize-migrating-h5ad" class="mdi mdi-checkbox-blank-outline"></i>
</span>
<span>Migrate data files</span>
</span>
</li>
</li>
<li>
<span class="icon-text">
<span class="icon">
<i class="mdi mdi-checkbox-blank-outline"></i>
</span>
<span>Setting access rights</span>
</span>
</li>
</li>
</ul>
<div class="field">
<div class="control mt-4">
<button class="button is-primary" id="dataset-finalize-next-step" disabled>
Done! Last step
<span class="icon pl-3">
<i class="mdi mdi-chevron-right"></i>
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="step-curate-dataset-c" class="step-c is-hidden">
</div>
</div>



</div> <!-- end #logged-in-c -->

</section><!-- end #content-c -->
Expand Down

0 comments on commit 96762a7

Please sign in to comment.