@openneuro/app 4.17.2 → 4.18.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openneuro/app",
3
- "version": "4.17.2",
3
+ "version": "4.18.1",
4
4
  "description": "React JS web frontend for the OpenNeuro platform.",
5
5
  "license": "MIT",
6
6
  "main": "public/client.js",
@@ -19,9 +19,9 @@
19
19
  "@elastic/apm-rum": "5.11.0",
20
20
  "@emotion/react": "11.6.0",
21
21
  "@emotion/styled": "11.6.0",
22
- "@niivue/niivue": "0.23.1",
23
- "@openneuro/client": "^4.17.2",
24
- "@openneuro/components": "^4.17.2",
22
+ "@niivue/niivue": "0.33.1",
23
+ "@openneuro/client": "^4.18.1",
24
+ "@openneuro/components": "^4.18.1",
25
25
  "bids-validator": "1.10.0",
26
26
  "bytes": "^3.0.0",
27
27
  "comlink": "^4.0.5",
@@ -81,5 +81,5 @@
81
81
  "publishConfig": {
82
82
  "access": "public"
83
83
  },
84
- "gitHead": "7e0baf02f32539a1ca3c2bf146e9e5f7ba437854"
84
+ "gitHead": "fbd8afe9033abf11e39e17968b3e9f60261a9c01"
85
85
  }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Generate a download script for this dataset
3
+ */
4
+ import React from 'react'
5
+ import { useLazyQuery, gql } from '@apollo/client'
6
+
7
+ function inlineDownload(filename, data): void {
8
+ const element = document.createElement('a')
9
+ element.setAttribute(
10
+ 'href',
11
+ 'data:text/x-shellscript;charset=utf-8,' + encodeURIComponent(data),
12
+ )
13
+ element.setAttribute('download', filename)
14
+
15
+ element.style.display = 'none'
16
+ document.body.appendChild(element)
17
+
18
+ element.click()
19
+
20
+ document.body.removeChild(element)
21
+ }
22
+
23
+ function generateDownloadScript(data): string {
24
+ let script = '#!/bin/sh\n'
25
+ for (const f of data.snapshot.downloadFiles) {
26
+ script += `curl --create-dirs ${f.urls[0]} -o ${f.filename}\n`
27
+ }
28
+ return script
29
+ }
30
+
31
+ interface DownloadS3DerivativesProps {
32
+ datasetId: string
33
+ snapshotTag: string
34
+ }
35
+
36
+ const getSnapshotDownload = gql`
37
+ query snapshot($datasetId: ID!, $tag: String!) {
38
+ snapshot(datasetId: $datasetId, tag: $tag) {
39
+ id
40
+ downloadFiles {
41
+ id
42
+ directory
43
+ filename
44
+ urls
45
+ size
46
+ }
47
+ }
48
+ }
49
+ `
50
+
51
+ export const DownloadScript = ({
52
+ datasetId,
53
+ snapshotTag,
54
+ }: DownloadS3DerivativesProps): JSX.Element => {
55
+ const [getDownload, { loading, data }] = useLazyQuery(getSnapshotDownload, {
56
+ variables: {
57
+ datasetId: datasetId,
58
+ tag: snapshotTag,
59
+ },
60
+ errorPolicy: 'all',
61
+ })
62
+ if (data) {
63
+ const script = generateDownloadScript(data)
64
+ inlineDownload(`${datasetId}-${snapshotTag}.sh`, script)
65
+ }
66
+ // This feature is only implemented for snapshots
67
+ if (snapshotTag) {
68
+ return (
69
+ <div>
70
+ <h4>Download with a shell script</h4>
71
+ <p>
72
+ A script is available to download with only curl. This may be useful
73
+ if your download environment makes it difficult to install DataLad or
74
+ Node.js.
75
+ </p>
76
+ <p>
77
+ {loading ? (
78
+ 'Loading...'
79
+ ) : (
80
+ <a
81
+ href="#"
82
+ onClick={(): void => {
83
+ void getDownload()
84
+ }}>
85
+ Download shell script
86
+ </a>
87
+ )}
88
+ </p>
89
+ </div>
90
+ )
91
+ } else {
92
+ return null
93
+ }
94
+ }
95
+
96
+ export default DownloadScript
@@ -139,6 +139,7 @@ const FileTree = ({
139
139
  datasetId={datasetId}
140
140
  snapshotTag={snapshotTag}
141
141
  path={path}
142
+ urls={file.urls}
142
143
  size={file.size}
143
144
  editMode={editMode}
144
145
  toggleFileToDelete={toggleFileToDelete}
@@ -91,6 +91,7 @@ interface FileProps {
91
91
  annexed: boolean
92
92
  annexKey: string
93
93
  datasetPermissions: string[]
94
+ urls: string[]
94
95
  toggleFileToDelete: ({
95
96
  id,
96
97
  path,
@@ -117,6 +118,7 @@ const File = ({
117
118
  datasetPermissions,
118
119
  toggleFileToDelete,
119
120
  isFileToBeDeleted,
121
+ urls,
120
122
  }: FileProps): JSX.Element => {
121
123
  const { icon, color } = getFileIcon(filename)
122
124
  const snapshotVersionPath = snapshotTag ? `/versions/${snapshotTag}` : ''
@@ -137,7 +139,10 @@ const File = ({
137
139
  <Tooltip tooltip={`Download: ${bytes.format(size) as string}`}>
138
140
  <span className="edit-file download-file">
139
141
  <a
140
- href={apiPath(datasetId, snapshotTag, filePath(path, filename))}
142
+ href={
143
+ urls?.[0] ||
144
+ apiPath(datasetId, snapshotTag, filePath(path, filename))
145
+ }
141
146
  download
142
147
  aria-label="download file">
143
148
  <i className="fa fa-download" />
@@ -8,6 +8,7 @@ import DownloadCommandLine from '../download/download-command-line.jsx'
8
8
  import DownloadDatalad from '../download/download-datalad.jsx'
9
9
  import { DatasetPageBorder } from './styles/dataset-page-border'
10
10
  import { HeaderRow3 } from './styles/header-row'
11
+ import { DownloadScript } from '../download/download-script'
11
12
 
12
13
  const DownloadDataset = ({ worker, datasetPermissions }) => {
13
14
  const { datasetId, tag: snapshotTag } = useParams()
@@ -34,6 +35,7 @@ const DownloadDataset = ({ worker, datasetPermissions }) => {
34
35
  workerId={workerId}
35
36
  datasetPermissions={datasetPermissions}
36
37
  />
38
+ <DownloadScript datasetId={datasetId} snapshotTag={snapshotTag} />
37
39
  </DatasetPageBorder>
38
40
  )
39
41
  }
@@ -5,6 +5,7 @@ import { Loading } from '@openneuro/components/loading'
5
5
  import Results from '../validation/validation-results.jsx'
6
6
  import UploaderContext from './uploader-context.js'
7
7
  import validate from '../workers/validate'
8
+ import schemaValidate from '../workers/schema'
8
9
 
9
10
  const UploadValidatorStatus = ({ issues, next, reset }) => {
10
11
  const errorCount = issues.errors.length
@@ -70,7 +71,11 @@ class UploadValidator extends React.Component {
70
71
  blacklistModalities: ['Microscopy'],
71
72
  },
72
73
  }
73
- validate(this.props.files, options).then(this.done)
74
+ if (this.props.schemaValidator) {
75
+ schemaValidate(this.props.files, options).then(this.done)
76
+ } else {
77
+ validate(this.props.files, options).then(this.done)
78
+ }
74
79
  }
75
80
 
76
81
  /**
@@ -115,12 +120,14 @@ UploadValidator.propTypes = {
115
120
  files: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
116
121
  next: PropTypes.func,
117
122
  reset: PropTypes.func,
123
+ schemaValidator: PropTypes.bool,
118
124
  }
119
125
 
120
126
  const UploadIssues = () => (
121
127
  <UploaderContext.Consumer>
122
128
  {uploader => (
123
129
  <UploadValidator
130
+ schemaValidator={uploader.schemaValidator}
124
131
  files={uploader.selectedFiles}
125
132
  next={() => uploader.setLocation('/upload/metadata')}
126
133
  reset={() => uploader.setLocation('/upload')}
@@ -1,6 +1,7 @@
1
1
  import React from 'react'
2
2
  import FileSelect from './file-select'
3
3
  import UploaderContext from './uploader-context.js'
4
+ import AdminUser from '../authentication/admin-user'
4
5
 
5
6
  const UploadSelect = () => (
6
7
  <div>
@@ -20,6 +21,18 @@ const UploadSelect = () => (
20
21
  </a>{' '}
21
22
  to upload
22
23
  <FileSelect onChange={uploader.selectFiles} />
24
+ <AdminUser>
25
+ <input
26
+ type="checkbox"
27
+ id="schema-validator-checkbox"
28
+ onChange={uploader.toggleSchemaValidator}
29
+ defaultChecked={uploader.schemaValidator}
30
+ />
31
+ <label htmlFor="schema-validator-checkbox">
32
+ {' '}
33
+ Use schema based validator
34
+ </label>
35
+ </AdminUser>
23
36
  </div>
24
37
  )}
25
38
  </UploaderContext.Consumer>
@@ -67,6 +67,10 @@ export class UploadClient extends React.Component {
67
67
  failedFiles: new Set(),
68
68
  // Abort controller for abandoning the upload
69
69
  abortController: null,
70
+ // Enable the new schema validator
71
+ schemaValidator: false,
72
+ // Toggle schemaValidator flag
73
+ toggleSchemaValidator: this.toggleSchemaValidator,
70
74
  }
71
75
  }
72
76
 
@@ -80,6 +84,13 @@ export class UploadClient extends React.Component {
80
84
  this.setState({ location: locationFactory(path) })
81
85
  }
82
86
 
87
+ /**
88
+ * Enable or disable schema based validation
89
+ */
90
+ toggleSchemaValidator = () => {
91
+ this.setState({ schemaValidator: !this.state.schemaValidator })
92
+ }
93
+
83
94
  /**
84
95
  * Specify a dataset to resume upload for
85
96
  * @param {string} datasetId