Skip to content
Merged
4 changes: 2 additions & 2 deletions docs/source/UserGuideV2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ portal can accessed at
- subject_id: The LabTracks ID of the mouse
- acq_datetime: The date and time the data was acquired. Should be
in ISO format, for example, 2024-05-27T16:07:59
- platform: Standardized way of collecting and processing data
(chosen from drop down menu)
- **modalities**: Two columns must be added per modality. A
**modality** (chosen from drop down menu) and a Posix style path
to the data source. For example,
Expand All @@ -140,6 +138,8 @@ portal can accessed at

- Optional fields

- platform: Standardized way of collecting and processing data
(chosen from drop down menu)
- metadata_dir: If metadata files are pre-compiled and saved to a
directory, you can add the Posix style path to the directory under
this column
Expand Down
113 changes: 94 additions & 19 deletions src/aind_data_transfer_service/templates/index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.8.1/font/bootstrap-icons.min.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<meta charset="UTF-8">
<title>{% block title %} {% endblock %} AIND Data Transfer Service</title>
<style>
Expand All @@ -25,7 +28,7 @@
background-color: #E8EAF6;
color: #3F51B5;
}
#submit {
#submit {
border: none;
border-radius: 6px;
color: #ffffff;
Expand Down Expand Up @@ -85,6 +88,22 @@ <h2>Submit Jobs</h2>
<button id="submit" type="button" onclick="submitJobs()">Submit</button>
<div id="message"></div><br>
<div id="response"></div>

<!-- Modality Settings Modal -->
<div class="modal fade" id="json-modal" tabindex="-1" aria-labelledby="json-modal-label">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header p-2">
<h5 class="modal-title" id="json-modal-label">json</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-2">
<!-- <div id="json-content" class="w-100" style="height:400px; border:none;"></div> -->
<pre id="json-content" style="white-space: pre-wrap; font-size: small;"></pre>
</div>
</div>
</div>
</div>
<script>
var jobs = []
var parsing_errors = []
Expand All @@ -106,29 +125,55 @@ <h2>Submit Jobs</h2>
$("#message").addClass("error");
}
};
addTableRow = function(data, table, tr, td, isHeader) {
addTableRow = function(data, table, tr, td, isHeader, modalitiesLength = null, job = null) {
Comment thread
helen-m-lin marked this conversation as resolved.
Outdated
tr = document.createElement('tr');


// Other columns
for (var d of data) {
td = document.createElement(isHeader ? 'th' : 'td');
td.innerHTML = d.value ?? d;
if (d.rowspan) {
td.setAttribute("rowspan", d.rowspan);
}
tr.append(td);
tr.appendChild(td);
}
// Last column displays full job json
if (modalitiesLength) {
const tdModal = document.createElement(isHeader ? 'th' : 'td');
if (!isHeader) {
Comment thread
helen-m-lin marked this conversation as resolved.
Outdated
const openModalButton = document.createElement("button");
openModalButton.type = "button";
openModalButton.className = "btn btn-outline-primary btn-sm";
openModalButton.dataset.bsToggle = "modal";
openModalButton.dataset.bsTarget = "#json-modal";
openModalButton.innerHTML = `<i class="bi bi-box-arrow-up-right"></i>`;
openModalButton.dataset.jobJSON = JSON.stringify(job, null, 2);

tdModal.appendChild(openModalButton);

// Apply rowspan if more than one modality row
tdModal.setAttribute("rowspan", modalitiesLength);

}
tr.appendChild(tdModal);
}
table.appendChild(tr);
};

validateJobs = function(fileElement) {
if (fileElement.files.length != 1) {
// File attach was cancelled by user
return;
}
}
if (![".csv", ".xlsx"].some(ext => fileElement.files[0].name.endsWith(ext))) {
fileElement.value = null;
alert("Invalid file type. Please attach a .csv or .xlsx file.");
return;
}

var formData = new FormData(document.getElementById("file_form"));

$.ajax({
url: "/api/v2/validate_csv",
type: "POST",
Expand All @@ -143,57 +188,64 @@ <h2>Submit Jobs</h2>
success: function(data) {
setMessage(msgTypes.validateSuccess);
jobs = data["data"]["jobs"];
parsing_errors = []
parsing_errors = [];
let jobsLength = jobs.length;
var table = document.createElement('table'), tr, td, row;

// Header row
addTableRow(
[ "job_type", "project_name", "s3_bucket", "platform", "subject_id", "acq_datetime", "metadata_dir", "modality", "modality.input_source" ],
["job_type", "project_name", "s3_bucket", "platform", "subject_id", "acq_datetime", "metadata_dir", "modality", "modality.input_source", "json"],
table, tr, td, true
);

for (row = 0; row < jobsLength; row++) {
let job = jobs[row];
let modalities = job.tasks?.modality_transformation_settings;
if (modalities) {
modalities = Object.entries(modalities).map(([key, value]) => ({
abbreviation: key,
input_source: value.job_settings?.input_source
}))
}));
}

let modalitiesJobJSON = job
let modalitiesLength = modalities ? modalities.length: 0;
let metadata_dir = job.tasks?.gather_preliminary_metadata?.job_settings?.metadata_dir
let metadata_dir = job.tasks?.gather_preliminary_metadata?.job_settings?.metadata_dir;

// First row: job info + first modality
addTableRow(
[
{ value: job.job_type, rowspan: modalitiesLength },
{ value: job.project_name, rowspan: modalitiesLength },
{ value: job.s3_bucket, rowspan: modalitiesLength },
{ value: job.platform.abbreviation, rowspan: modalitiesLength },
{ value: job.platform?.abbreviation ?? "", rowspan: modalitiesLength },
{ value: job.subject_id, rowspan: modalitiesLength },
{ value: job.acq_datetime, rowspan: modalitiesLength },
{ value: metadata_dir ?? "", rowspan: modalitiesLength },
modalities ? modalities[0].abbreviation : "",
modalities ? modalities[0].input_source : ""
], table, tr, td, false
], table, tr, td, false, modalitiesLength, job
);
for (mRow = 1; mRow < modalitiesLength; mRow++) {
let modality = modalities[mRow]
for (let mRow = 1; mRow < modalities.length; mRow++) {
let modality = modalities[mRow];
addTableRow(
[ modality.abbreviation, modality.input_source ],
table, tr, td, false
);
}
}

$("#response").html(table);
},
error: function(data) {
jobs = []
parsing_errors = data.responseJSON["data"]["errors"]
jobs = [];
parsing_errors = data.responseJSON["data"]["errors"];
setMessage(msgTypes.validateError);
$("#response").html(parsing_errors.map((err) => {
return `<li>${err}</li>`
}));
$("#response").html(parsing_errors.map(err => `<li>${err}</li>`));
}
});
};
};

submitJobs = function() {
if(jobs.length > 0 && parsing_errors.length == 0){
let job_settings = {};
Expand Down Expand Up @@ -253,6 +305,29 @@ <h2>Submit Jobs</h2>
return;
}
};

document.addEventListener("DOMContentLoaded", () => {
var jsonModal = document.getElementById('json-modal');

jsonModal.addEventListener('show.bs.modal', function(event) {
var button = event.relatedTarget;
if (!button) return;

var jobJSON = button.dataset.jobJSON;
console.log("Job JSON:", jobJSON)

var jsonContentDiv = document.getElementById("json-content");
// const blob = new Blob(
// [`<pre style="white-space: pre-wrap; font-size: small;">${JSON.stringify(modalities, null, 2)}</pre>`],
// { type: "text/html" }
// );
jsonContentDiv.textContent = jobJSON;
});

jsonModal.addEventListener('hidden.bs.modal', function() {
document.getElementById("json-content").textContent = '';
});
});
</script>
</body>
</html>
</html>