aind-data-transfer-service 1.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of aind-data-transfer-service might be problematic. Click here for more details.

@@ -0,0 +1,323 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
6
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.8.1/font/bootstrap-icons.min.css" rel="stylesheet">
7
+ <link rel="stylesheet" href="https://cdn.datatables.net/2.1.8/css/dataTables.dataTables.css" />
8
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.3/moment.min.js"></script>
10
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
11
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
12
+ <script src="https://cdn.datatables.net/2.1.8/js/dataTables.js"></script>
13
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
14
+ <title>{% block title %} {% endblock %} AIND Data Transfer Service Jobs</title>
15
+ <style>
16
+ body {
17
+ margin: 20px;
18
+ font-family: arial, sans-serif;
19
+ }
20
+ nav {
21
+ height: 40px;
22
+ }
23
+ .modal-body {
24
+ height: calc(100vh - 100px);
25
+ }
26
+ </style>
27
+ </head>
28
+ <body>
29
+ <nav>
30
+ <a href="/">Submit Jobs</a> |
31
+ <a href="/jobs">Job Status</a> |
32
+ <a href="/job_params">Job Parameters</a> |
33
+ <a title="Download job template as .xslx" href= "/api/job_upload_template" download>Job Submit Template</a> |
34
+ <a title="List of project names" href= "{{ project_names_url }}" target="_blank" >Project Names</a> |
35
+ <a title="For more information click here" href="https://aind-data-transfer-service.readthedocs.io" target="_blank" >Help</a>
36
+ </nav>
37
+ <div class="content">
38
+ <!-- display total entries -->
39
+ <h4 class="mb-2">Jobs Submitted: <span id="total-entries"></span></h4>
40
+ <!-- filters for job status results-->
41
+ <div class="card mb-4 small" style="width:400px">
42
+ <div class="card-header py-1" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-filters" aria-expanded="false" aria-controls="collapse-filters">
43
+ <i class="bi bi-filter"></i><span class="ms-2">Filter by</span>
44
+ <i class="bi bi-chevron-expand float-end"></i>
45
+ </div>
46
+ <div id="collapse-filters" class="collapse card-body p-2">
47
+ <!-- filter by job status -->
48
+ <div class="input-group input-group-sm mb-1">
49
+ <span class="input-group-text" style="width:35%">Status</span>
50
+ <select class="form-select" onchange="filterJobsByColumn(4, this.value);this.blur();">
51
+ {% for s in [
52
+ {"label": "all", "value": "", "class": "text-dark"},
53
+ {"label": "queued", "value": "queued", "class": "text-secondary"},
54
+ {"label": "running", "value": "running", "class": "text-info"},
55
+ {"label": "failed", "value": "failed", "class": "text-danger"},
56
+ {"label": "success", "value": "success", "class": "text-success"},
57
+ ] %}
58
+ <option class="{{ s.class }}" value="{{ s.value }}">{{ s.label }}</option>
59
+ {% endfor %}
60
+ </select>
61
+ </div>
62
+ <!-- filter by job submitted date range -->
63
+ <div class="input-group input-group-sm">
64
+ <span class="input-group-text" style="width:35%">Submit Time</span>
65
+ <input id="submit-date-range" class="form-select" type="text" />
66
+ </div>
67
+ <!-- filter by asset name -->
68
+ <hr class="flex-grow-1 border-secondary">
69
+ <div class="input-group input-group-sm mb-1">
70
+ <span class="input-group-text" style="width:35%">Asset Name</span>
71
+ <input id="filter-name-input" type="text" class="form-control" placeholder="asset name" oninput="filterJobsByColumn(0, this.value)">
72
+ <button id="filter-name-clear" class="btn btn-outline-secondary" type="button" title="Clear" onclick="clearFilterJobsByColumn(0, '#filter-name-input')">
73
+ <i class="bi bi-x-lg"></i>
74
+ </button>
75
+ </div>
76
+ <!-- filter by job id-->
77
+ <div class="input-group input-group-sm mb-1">
78
+ <span class="input-group-text" style="width:35%">Job ID</span>
79
+ <input id="filter-id-input" type="text" class="form-control" placeholder="job id" oninput="filterJobsByColumn(1, this.value)">
80
+ <button id="filter-id-clear" class="btn btn-outline-secondary" type="button" title="Clear" onclick="clearFilterJobsByColumn(1, '#filter-id-input')">
81
+ <i class="bi bi-x-lg"></i>
82
+ </button>
83
+ </div>
84
+ <!-- filter by job type-->
85
+ <div class="input-group input-group-sm mb-1">
86
+ <span class="input-group-text" style="width:35%">Job Type</span>
87
+ <input id="filter-job-type-input" type="text" class="form-control" placeholder="job type" oninput="filterJobsByColumn(2, this.value)">
88
+ <button id="filter-job-type-clear" class="btn btn-outline-secondary" type="button" title="Clear" onclick="clearFilterJobsByColumn(2, '#filter-job-type-input')">
89
+ <i class="bi bi-x-lg"></i>
90
+ </button>
91
+ </div>
92
+ <!-- filter by dag id -->
93
+ <div class="input-group input-group-sm mb-1">
94
+ <span class="input-group-text" style="width:35%">Dag ID</span>
95
+ <select class="form-select" onchange="filterJobsByColumn(3, this.value);this.blur();">
96
+ <option class="text-dark" value="">all</option>
97
+ {% for dag_id in dag_ids %}
98
+ <option class="text-secondary" value="^{{ dag_id }}$">{{ dag_id }}</option>
99
+ {% endfor %}
100
+ </select>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ <!-- job status table -->
105
+ <div>
106
+ <table id="searchJobsTable" class="display compact table table-bordered table-sm" style="font-size: small">
107
+ <thead>
108
+ <tr>
109
+ <th>Asset Name</th>
110
+ <th>Job ID</th>
111
+ <th>Job Type</th>
112
+ <th>Dag ID</th>
113
+ <th>Status</th>
114
+ <th>Submit Time</th>
115
+ <th>Start Time</th>
116
+ <th>End Time</th>
117
+ <th>Comment</th>
118
+ <th>Tasks</th>
119
+ </tr>
120
+ </thead>
121
+ </table>
122
+ <!-- modal for displaying tasks per job from full jobs table -->
123
+ <div class="modal fade" id="tasks-modal-full" tabindex="-1" aria-labelledby="tasks-modal-full-label" aria-hidden="true">
124
+ <div class="modal-dialog modal-xl">
125
+ <div class="modal-content">
126
+ <div class="modal-header p-2">
127
+ <div class="modal-title fw-bold" id="tasks-modal-full-label" style="font-size: small">
128
+ <span id="modal-title-job-name" class="me-2"></span>
129
+ <span id="modal-title-job-id" class="me-2"></span>
130
+ <span id="modal-title-job-state" class="badge"></span>
131
+ </div>
132
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
133
+ </div>
134
+ <div class="modal-body p-2">
135
+ <iframe id="tasks-iframe" class="w-100 h-100" src=""></iframe>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ <script>
143
+ // User can filter for jobs by status and submit time, and view results.
144
+ // Full Jobs Table: Loads all jobs using DataTables library. Enables searching across all jobs.
145
+ let tableUrl = new URL("{{ url_for('get_job_status_list') }}");
146
+ $(document).ready(function() {
147
+ const today = moment();
148
+ const twoWeeksAgo = moment().subtract(13, 'days');
149
+
150
+ // initialize daterangepicker for submit date filter
151
+ $('#submit-date-range').daterangepicker({
152
+ startDate: twoWeeksAgo,
153
+ endDate: today,
154
+ minDate: twoWeeksAgo,
155
+ maxDate: today,
156
+ ranges: {
157
+ 'Today': [today, today],
158
+ 'Last 3 Days': [moment().subtract(2, 'days'), today],
159
+ 'Last 7 Days': [moment().subtract(6, 'days'), today],
160
+ 'Last 14 Days': [twoWeeksAgo, today],
161
+ }
162
+ }, filterJobsBySubmitTimeRange);
163
+
164
+ // initialize job status table with default values
165
+ const initialParams = {
166
+ // set default submit date range client-side for browser's local time
167
+ execution_date_gte: twoWeeksAgo.startOf('day').toISOString(),
168
+ execution_date_lte: today.endOf('day').toISOString(),
169
+ };
170
+ updateJobStatusTable(initialParams);
171
+
172
+ // tasks modal for full jobs table
173
+ var tasksModal = document.getElementById('tasks-modal-full');
174
+ tasksModal.addEventListener('show.bs.modal', function (event) {
175
+ var sourceData = event.relatedTarget?.dataset;
176
+ updateJobTasksModal(sourceData?.dagId, sourceData?.jobId, sourceData?.jobName, sourceData?.jobState);
177
+ })
178
+ tasksModal.addEventListener('hidden.bs.modal', function (event) {
179
+ updateJobTasksModal(null, null, null, null);
180
+ })
181
+ });
182
+ // FULL JOBS TABLE -----------------------------------------------
183
+ // Helper functions for custom column rendering
184
+ function renderJobStatus(cell, cellData, rowData, rowIndex, colIndex) {
185
+ let customClass = cellData === 'success' ? 'table-success'
186
+ : cellData === 'failed' ? 'table-danger'
187
+ : cellData === 'running' ? 'table-info'
188
+ : cellData === 'queued' ? 'table-secondary'
189
+ : null;
190
+ if (customClass) {
191
+ cell.classList.add(customClass);
192
+ }
193
+ }
194
+ function renderDatetime(data, type, row) {
195
+ return (type === 'display') ? moment.utc(data).local().format('YYYY-MM-DD h:mm:ss a') : data;
196
+ }
197
+ function renderTasksButton(data, type, row) {
198
+ if (type == 'display') {
199
+ return (`<button type="button" class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#tasks-modal-full" data-dag-id="${row.dag_id}" data-job-id="${data}" data-job-name="${row.name}" data-job-state="${row.job_state}">
200
+ <i class="bi bi-box-arrow-up-right" title="View tasks and logs"></i>
201
+ </button>`);
202
+ }
203
+ return data;
204
+ }
205
+ // Create DataTable for full jobs table
206
+ function createFullJobsTable() {
207
+ $('#searchJobsTable').DataTable({
208
+ ajax: {
209
+ url: tableUrl,
210
+ dataSrc: 'data.job_status_list'
211
+ },
212
+ processing: true,
213
+ columns: [
214
+ { data: 'name', searchable: true },
215
+ { data: 'job_id', searchable: true },
216
+ { data: 'job_type', searchable: true },
217
+ { data: 'dag_id', searchable: true },
218
+ { data: 'job_state', searchable: true, createdCell: renderJobStatus },
219
+ { data: 'submit_time', searchable: false, render: renderDatetime },
220
+ { data: 'start_time', searchable: false, render: renderDatetime },
221
+ { data: 'end_time', searchable: false, render: renderDatetime },
222
+ { data: 'comment', searchable: false, defaultContent: 'None' },
223
+ { data: 'job_id', searchable: false, render: renderTasksButton },
224
+ ],
225
+ initComplete: (settings, json) => updateJobsCount(json),
226
+ // layout options
227
+ pageLength: 25,
228
+ order: [5, 'desc'], // submit time descending
229
+ layout: {
230
+ topStart: null,
231
+ topEnd: null,
232
+ bottomStart: null,
233
+ bottomEnd: null,
234
+ top: [
235
+ 'pageLength',
236
+ 'info',
237
+ { paging: { numbers: false } }
238
+ ],
239
+ },
240
+ language: {
241
+ info: "_START_ to _END_ of _TOTAL_",
242
+ infoEmpty: "0 to 0 of 0",
243
+ entries: { _: "jobs", 1: "job" },
244
+ paginate: {
245
+ first: '&laquo; First',
246
+ previous: '&lsaquo; Prev',
247
+ next: 'Next &rsaquo;',
248
+ last: 'Last &raquo;'
249
+ },
250
+ },
251
+ });
252
+ }
253
+ // Modal for tasks per job from full jobs table
254
+ function updateJobTasksModal(dagId, jobId, jobName, jobState) {
255
+ // Update the modal header with the job id and name
256
+ document.getElementById('modal-title-job-id').textContent = `(${jobId})`;
257
+ document.getElementById('modal-title-job-name').textContent = jobName;
258
+ var modalTitleJobState = document.getElementById('modal-title-job-state');
259
+ modalTitleJobState.textContent = jobState;
260
+ if (jobState) {
261
+ modalTitleJobState.classList.add(
262
+ jobState === 'success' ? 'bg-success'
263
+ : jobState === 'failed' ? 'bg-danger'
264
+ : jobState === 'running' ? 'bg-info'
265
+ : 'bg-secondary'
266
+ );
267
+ } else {
268
+ modalTitleJobState.classList.value = 'badge';
269
+ }
270
+ // Update the iframe src with the job id
271
+ var tasksIframe = document.getElementById('tasks-iframe');
272
+ if (jobId) {
273
+ var url = new URL("{{ url_for('job_tasks_table') }}");
274
+ url.searchParams.append('dag_id', dagId);
275
+ url.searchParams.append('dag_run_id', jobId);
276
+ tasksIframe.src = url;
277
+ } else {
278
+ tasksIframe.src = "";
279
+ }
280
+ }
281
+ // EVENT HANDLERS ------------------------------------------------
282
+ function updateJobStatusTable(newParams) {
283
+ Object.entries(newParams).forEach(([key, value]) => {
284
+ tableUrl.searchParams.set(key, value);
285
+ });
286
+ if (DataTable.isDataTable('#searchJobsTable')) {
287
+ // load the table with new params and update total entries
288
+ $('#searchJobsTable').DataTable().ajax.url(tableUrl.toString()).load(
289
+ (data) => updateJobsCount(data)
290
+ );
291
+ } else {
292
+ createFullJobsTable();
293
+ }
294
+ }
295
+ function updateJobsCount(data) {
296
+ // data is the response from the server after ajax call
297
+ $('#total-entries').text(data?.data?.total_entries);
298
+ }
299
+ // Filters
300
+ function filterJobsByColumn(columnIndex, value) {
301
+ if (columnIndex == 3 || columnIndex == 4) {
302
+ //exact match for dag_id and job_state dropdowns
303
+ $('#searchJobsTable').DataTable().columns(columnIndex).search(value.trim(), true, false).draw();
304
+ } else {
305
+ // smart match
306
+ $('#searchJobsTable').DataTable().columns(columnIndex).search(value.trim()).draw();
307
+ }
308
+ }
309
+ function clearFilterJobsByColumn(columnIndex, inputElementId) {
310
+ $(inputElementId).val('');
311
+ filterJobsByColumn(columnIndex, '');
312
+ }
313
+ function filterJobsBySubmitTimeRange(start, end) {
314
+ // This filter is the only one that sends a new ajax request to the server
315
+ // NOTE: daterangepicker already has 00:00:00.000 and 23:59:59.999 for start and end
316
+ updateJobStatusTable({
317
+ execution_date_gte: start.toISOString(),
318
+ execution_date_lte: end.toISOString(),
319
+ });
320
+ }
321
+ </script>
322
+ </body>
323
+ </html>
@@ -0,0 +1,146 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
7
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.8.1/font/bootstrap-icons.min.css" rel="stylesheet">
8
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.3/moment.min.js"></script>
10
+ </head>
11
+
12
+ <body>
13
+ <!-- tasks table -->
14
+ <table class="table table-bordered table-striped table-hover table-sm" style="font-size: small">
15
+ <tr>
16
+ <th>Task ID</th>
17
+ <th>Try Number</th>
18
+ <th>Map Index</th>
19
+ <th>Status</th>
20
+ <th>Submit Time</th>
21
+ <th>Start Time</th>
22
+ <th>End Time</th>
23
+ <th>Duration</th>
24
+ <th>Logs</th>
25
+ </tr>
26
+ {% for job_task in job_tasks_list %}
27
+ <tr>
28
+ <td>{{job_task.task_id}}</td>
29
+ <td>{{job_task.try_number}}</td>
30
+ <td>
31
+ {% if job_task.map_index > -1 %}
32
+ <span>{{job_task.map_index}}</span>
33
+ {% else %}
34
+ <span></span>
35
+ {% endif %}
36
+ </td>
37
+ <td class="{% if job_task.task_state == 'success' %}table-success
38
+ {% elif job_task.task_state == 'failed' %}table-danger
39
+ {% elif job_task.task_state == 'running' %}table-info
40
+ {% elif job_task.task_state == 'queued' %}table-secondary
41
+ {% endif %}">{{job_task.task_state}}</td>
42
+ <td class="datetime_to_be_adjusted">{{job_task.submit_time}}</td>
43
+ <td class="datetime_to_be_adjusted">{{job_task.start_time}}</td>
44
+ <td class="datetime_to_be_adjusted">{{job_task.end_time}}</td>
45
+ <td>{{job_task.duration}}</td>
46
+ <td>
47
+ {% if job_task.try_number > 0 %}
48
+ <button type="button" class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#logs-modal"
49
+ data-dag-id="{{ job_task.dag_id }}"
50
+ data-job-id="{{ job_task.job_id }}"
51
+ data-task-id="{{ job_task.task_id }}"
52
+ data-task-state="{{ job_task.task_state }}"
53
+ data-task-try-number="{{ job_task.try_number }}"
54
+ data-task-map-index="{{ job_task.map_index }}"
55
+ ><i class="bi bi-box-arrow-up-right" title="View logs"></i>
56
+ </button>
57
+ {% endif %}
58
+ </td>
59
+ </tr>
60
+ {% endfor %}
61
+ </table>
62
+ <!-- modal for displaying logs per task -->
63
+ <div class="modal fade" id="logs-modal" tabindex="-1" aria-labelledby="logs-modal-label" aria-hidden="true">
64
+ <div class="modal-dialog modal-fullscreen">
65
+ <div class="modal-content">
66
+ <div class="modal-header p-2">
67
+ <div class="modal-title fw-bold" id="logs-modal-label" style="font-size: small">
68
+ <span id="modal-title-task" class="me-2"></span><span id="modal-title-task-state" class="badge"></span>
69
+ </div>
70
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
71
+ </div>
72
+ <div class="modal-body p-2">
73
+ <iframe id="logs-iframe" class="w-100 h-100" src=""></iframe>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ <!-- display errors to user -->
79
+ {% if status_code != 200 %}
80
+ <div class="alert alert-danger" role="alert">
81
+ <h4 class="alert-heading">{{ message }}</h4>
82
+ <p>
83
+ {% for error in errors %}
84
+ {% if error is string %}{{ error }}
85
+ {% elif error is mapping %}
86
+ {% for key, value in error.items() %}
87
+ <strong>{{ key }}:</strong> {{ value }}<br>
88
+ {% endfor %}
89
+ {% endif %}
90
+ {% endfor %}
91
+ </p>
92
+ </div>
93
+ {% endif %}
94
+ <script>
95
+ window.onload = function () {
96
+ document.querySelectorAll(".datetime_to_be_adjusted").forEach(function (el) {
97
+ if (el.innerHTML !== "") {
98
+ var utcTime = moment.utc(el.innerText); // This is the time in UTC
99
+ utcTime.local(); // Switch to using the browser's local timezone
100
+ el.innerText = utcTime.format('YYYY-MM-DD h:mm:ss a'); // Write the local time back to the element
101
+ };
102
+ });
103
+ var logsModal = document.getElementById('logs-modal');
104
+ logsModal.addEventListener('show.bs.modal', function (event) {
105
+ var sourceData = event.relatedTarget?.dataset;
106
+ updateTaskLogsModal(sourceData?.dagId,sourceData?.jobId, sourceData?.taskId, sourceData?.taskState, sourceData?.taskTryNumber, sourceData?.taskMapIndex);
107
+ })
108
+ logsModal.addEventListener('hidden.bs.modal', function (event) {
109
+ updateTaskLogsModal(null, null, null, null, null, null);
110
+ })
111
+ }
112
+ function updateTaskLogsModal(dagId, jobId, taskId, taskState, taskTryNumber, taskMapIndex) {
113
+ console.log('updateTaskLogsModal', dagId, jobId, taskId, taskState, taskTryNumber, taskMapIndex);
114
+ // Update the modal header with the task id, try number, map index, and state
115
+ var tryMapLabel = taskMapIndex > -1 ? `${taskId} (try ${taskTryNumber}, map_index ${taskMapIndex})` : `${taskId} (try ${taskTryNumber})`;
116
+ document.getElementById('modal-title-task').textContent = tryMapLabel;
117
+ var modalTitleTaskState = document.getElementById('modal-title-task-state');
118
+ modalTitleTaskState.textContent = taskState;
119
+ if (taskState) {
120
+ modalTitleTaskState.classList.add(
121
+ taskState === 'success' ? 'bg-success'
122
+ : taskState === 'failed' ? 'bg-danger'
123
+ : taskState === 'running' ? 'bg-info'
124
+ : 'bg-secondary'
125
+ );
126
+ } else {
127
+ modalTitleTaskState.classList.value = 'badge';
128
+ }
129
+ // Update the iframe src to get the logs for the task
130
+ var logsIframe = document.getElementById('logs-iframe');
131
+ if (jobId) {
132
+ var url = new URL("{{ url_for('task_logs') }}");
133
+ url.searchParams.append('dag_id', dagId);
134
+ url.searchParams.append('dag_run_id', jobId);
135
+ url.searchParams.append('task_id', taskId);
136
+ url.searchParams.append('try_number', taskTryNumber);
137
+ url.searchParams.append('map_index', taskMapIndex);
138
+ logsIframe.src = url;
139
+ } else {
140
+ logsIframe.src = "";
141
+ }
142
+ }
143
+ </script>
144
+ </body>
145
+
146
+ </html>
@@ -0,0 +1,31 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
6
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
7
+ </head>
8
+ <body>
9
+ {% if status_code == 200 %}
10
+ <!-- display task logs -->
11
+ <div>
12
+ <pre class="bg-light p-1 border rounded" style="font-size: x-small">{{logs}}</pre>
13
+ </div>
14
+ {% else %}
15
+ <!-- display errors to user -->
16
+ <div class="alert alert-danger" role="alert">
17
+ <h4 class="alert-heading">{{ message }}</h4>
18
+ <p>
19
+ {% for error in errors %}
20
+ {% if error is string %}{{ error }}
21
+ {% elif error is mapping %}
22
+ {% for key, value in error.items() %}
23
+ <strong>{{ key }}:</strong> {{ value }}<br>
24
+ {% endfor %}
25
+ {% endif %}
26
+ {% endfor %}
27
+ </p>
28
+ </div>
29
+ {% endif %}
30
+ </body>
31
+ </html>
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.4
2
+ Name: aind-data-transfer-service
3
+ Version: 1.12.0
4
+ Summary: Service that handles requests to upload data to the cloud
5
+ Author: Allen Institute for Neural Dynamics
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: boto3
12
+ Requires-Dist: boto3-stubs[ssm]
13
+ Requires-Dist: pydantic<2.9,>=2.7
14
+ Requires-Dist: pydantic-settings>=2.0
15
+ Requires-Dist: aind-data-schema<2.0,>=1.0.0
16
+ Requires-Dist: aind-data-transfer-models==0.17.0
17
+ Requires-Dist: aind-metadata-mapper==0.23.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: aind-data-transfer-service[server]; extra == "dev"
20
+ Requires-Dist: black; extra == "dev"
21
+ Requires-Dist: coverage; extra == "dev"
22
+ Requires-Dist: flake8; extra == "dev"
23
+ Requires-Dist: interrogate; extra == "dev"
24
+ Requires-Dist: isort; extra == "dev"
25
+ Provides-Extra: docs
26
+ Requires-Dist: Sphinx; extra == "docs"
27
+ Requires-Dist: furo; extra == "docs"
28
+ Provides-Extra: server
29
+ Requires-Dist: fastapi; extra == "server"
30
+ Requires-Dist: httpx; extra == "server"
31
+ Requires-Dist: jinja2; extra == "server"
32
+ Requires-Dist: starlette; extra == "server"
33
+ Requires-Dist: starlette_wtf; extra == "server"
34
+ Requires-Dist: uvicorn[standard]; extra == "server"
35
+ Requires-Dist: wtforms; extra == "server"
36
+ Requires-Dist: requests==2.25.0; extra == "server"
37
+ Requires-Dist: openpyxl; extra == "server"
38
+ Requires-Dist: python-logging-loki; extra == "server"
39
+ Dynamic: license-file
40
+
41
+ # aind-data-transfer-service
42
+
43
+ [![License](https://img.shields.io/badge/license-MIT-brightgreen)](LICENSE)
44
+ ![Code Style](https://img.shields.io/badge/code%20style-black-black)
45
+ [![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release)
46
+
47
+ This service can be used to upload data stored in a VAST drive. It uses FastAPI to upload a job submission csv file that will be used to trigger a data transfer job in an on-prem HPC. Based on the information provided in the file, the data upload process fetches the appropriate metadata and starts the upload process.
48
+
49
+ More information can be found at [readthedocs](https://aind-data-transfer-service.readthedocs.io).
@@ -0,0 +1,23 @@
1
+ aind_data_transfer_service/__init__.py,sha256=x8KPuByORZYNVuata2ndOXEyYaqruRRd98591uYweUs,272
2
+ aind_data_transfer_service/log_handler.py,sha256=c7a-gLmZeRpeCUBwCz6XsTszWXQeQdR7eKZtas4llXM,1700
3
+ aind_data_transfer_service/server.py,sha256=OL53huEa654aCjOWEafP9rV1Qp4l_qSX6R_Tsis_ing,41252
4
+ aind_data_transfer_service/configs/__init__.py,sha256=9W5GTuso9Is1B9X16RXcdb_GxasZvj6qDzOBDv0AbTc,36
5
+ aind_data_transfer_service/configs/csv_handler.py,sha256=-iB_6tRoD5sKA9K3CCcyFHHmnNSLN7bVnmS-vLsMMRE,2085
6
+ aind_data_transfer_service/configs/job_configs.py,sha256=T-h5N6lyY9xTZ_xg_5FxkyYuMdagApbE6xalxFQ-bqA,18848
7
+ aind_data_transfer_service/configs/job_upload_template.py,sha256=l1pZd_jT3dml0pdVr9Tjml8KRi5-LkHbSGIEAjFMgps,5096
8
+ aind_data_transfer_service/hpc/__init__.py,sha256=YNc68YNlmXwKIPFMIViz_K4XzVVHkLPEBOFyO5DKMKI,53
9
+ aind_data_transfer_service/hpc/client.py,sha256=-JSxAWn96_XOIDwhsXAHK3TZAdckddUhtcCzRHnaTqA,4700
10
+ aind_data_transfer_service/hpc/models.py,sha256=-7HhV16s_MUyKPy0x0FGIbnq8DPL2qJAzJO5G7003AE,16184
11
+ aind_data_transfer_service/models/__init__.py,sha256=Meym73bEZ9nQr4QoeyhQmV3nRTYtd_4kWKPNygsBfJg,25
12
+ aind_data_transfer_service/models/core.py,sha256=rVOCOBOv101w6fMXt5o9anxWlywMHSTL0uQT4u_T6H0,9985
13
+ aind_data_transfer_service/models/internal.py,sha256=MGQrPuHrR21nn4toqdTCIEDW6MG7pWRajoPqD3j-ST0,9706
14
+ aind_data_transfer_service/templates/index.html,sha256=8I4QLC4pAPKD3UUrrt3sUDP8Ynopf7B8YZWC7_VabjI,10624
15
+ aind_data_transfer_service/templates/job_params.html,sha256=vqIdNQsZTM0kq3Wa9u-VjmmMa0UzBTpK02WpOSatXBQ,8817
16
+ aind_data_transfer_service/templates/job_status.html,sha256=vIOaJGJM78hOWTLTAzMfHjG9sNqPvS-muAyXYQtpnYI,16901
17
+ aind_data_transfer_service/templates/job_tasks_table.html,sha256=rWFukhjZ4dhPyabe372tmi4lbQS2fyELZ7Awbn5Un4g,6181
18
+ aind_data_transfer_service/templates/task_logs.html,sha256=y1GnQft0S50ghPb2xJDjAlefymB9a4zYdMikUFV7Tl4,918
19
+ aind_data_transfer_service-1.12.0.dist-info/licenses/LICENSE,sha256=U0Y7B3gZJHXpjJVLgTQjM8e_c8w4JJpLgGhIdsoFR1Y,1092
20
+ aind_data_transfer_service-1.12.0.dist-info/METADATA,sha256=5HtVeGrYgmhrX1EwaIrqHcfi0VYhWigLdmtZCV_d2vQ,2236
21
+ aind_data_transfer_service-1.12.0.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
22
+ aind_data_transfer_service-1.12.0.dist-info/top_level.txt,sha256=XmxH0q27Jholj2-VYh-6WMrh9Lw6kkuCX_fdsj3SaFE,27
23
+ aind_data_transfer_service-1.12.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (79.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Allen Institute for Neural Dynamics
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ aind_data_transfer_service