-
File Upload
+
+
+
+ {{ dialog.title }}
+ {{ dialog.content }}
+
+ Close
+
+
+
+
+
+
Description
+
The simplest method to curate your sample into the database is by uploading an MS Excel spreadsheet. For each
+ sample, select or create a dataset for your sample group, upload a completed Excel template file using the
+ first uploading box and other supplementary image and raw data files using the second uploading box. The
+ master Excel template contains all possible fields for nanocomposite sample data and therefore many fields
+ will remain blank for your sample. Fill in only the parameters applicable to your sample. Customized templates
+ are available upon request, please contact the NanoMine team if
+ customization is required.
+
+
+
+
Steps
+
+ NOTE: Filesets for samples are grouped into datasets. The files for a sample (images, auxiliary spreadsheet
+ data, completed Excel template, etc) are uploaded as a set called a fileset. Uploading multiple samples
+ requires multiple fileset uploads.
+
+
+
+ Create a new dataset for the control sample and its related files, then when uploading each additional
+ sample be sure to select the same dataset that was used for the control sample of the sample group.
+
+
+ Click here to download the blank MS Excel template (137 kB). (Click here to see an example, 263 kB)
+
+
+ Fill in the parameters for all applicable cells in the Excel template file. Prepare the supplementary
+ images and raw data files.
+
+
+ Select the completed Excel template file in the first uploading box.
+
+
+ Select the supplementary images and other raw data files in the second uploading box (press "Ctrl" or
+ "Command" when selecting multiple files), then click Submit to upload your data.
+
+
+ Wait for the feedback message. Please read the message and follow the instructions if an error message is
+ displayed.
+
+
+
+
+
Note
+
+
+ We recommend you to upload your control sample first and remember its sample ID.
+
+
+ Upload one sample data at a time (one template Excel file along with supplementary files).
+
+
+ Rows or sections followed by a "#" sign in the template Excel file can be duplicated. Copy them into
+ additional rows if needed.
+
+
+ Acceptable image file format: JPG, PNG, TIF(F). Indicate the full image file name including the extensions
+ in the corresponding cells in the Excel template file.
+
+
+
+
+
Inputs
+
+
+
+
+
Select a completed Excel Template File
+ Browse
+
+
+
+ check_circle_outline {{ templateName }}
+
+
+
+
+ Select Other Files (including raw data files and image files)
+ Browse
+
+
+
+
+ check_circle_outline
+ {{ file.fileName }}
+
+
+
+
Submit
+
Go Back
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/pages/nanomine/xmlUploader/xmlUploader.js b/app/src/pages/nanomine/xmlUploader/xmlUploader.js
index a6ec5a6d..68ec10b3 100644
--- a/app/src/pages/nanomine/xmlUploader/xmlUploader.js
+++ b/app/src/pages/nanomine/xmlUploader/xmlUploader.js
@@ -1,5 +1,160 @@
+import { JobMgr } from '@/modules/JobMgr.js'
+// import { Auth } from '@/modules/Auth.js'
+import ReferenceContainer from '@/components/nanomine/ReferenceContainer.vue'
+import Dialog from '@/components/Dialog.vue'
+import DatasetViewer from '@/components/nanomine/DatasetViewer.vue'
+import { mapMutations, mapGetters } from 'vuex'
+
export default {
- name: 'XmlUploader',
+ name: 'XMLUploader',
+ components: {
+ ReferenceContainer,
+ dialogBox: Dialog,
+ DatasetViewer
+ },
+ data: () => ({
+ title: 'File Upload',
+ dialog: false,
+ templateName: '',
+ templateUrl: '',
+ template: null,
+ files: [],
+ filesDisplay: [],
+ templateUploaded: false,
+ jobId: '',
+ datasetOptions: { mineOnly: 'always' },
+ datasetSelected: null,
+ references: [
+ '10.1063/1.4943679',
+ '10.1063/1.5046839'
+ ],
+ auth: {
+ // AUTH MOCKED because auth is not yet implemented
+ isLoggedIn: () => false,
+ isTestUser: () => false
+ }
+ }),
+ computed: {
+ ...mapGetters({
+ dialogBoxActive: 'dialogBox'
+ })
+ },
+ beforeMount: function () {
+ // this.auth = new Auth()
+ if (!this.auth.isLoggedIn()) {
+ this.renderDialog('Login Required', 'Login is required before uploading files.')
+ }
+ },
+ methods: {
+ goBack () {
+ return window.history.go(-1)
+ },
+ datasetSelectedHandler (dataset) {
+ this.datasetSelected = dataset
+ },
+ setLoading: function () {
+ this.$store.commit('isLoading')
+ },
+ resetLoading: function () {
+ this.$store.commit('notLoading')
+ },
+ pickFile () {
+ this.$refs.myUpload.click()
+ },
+ pickTemplate () {
+ this.$refs.myTemplate.click()
+ },
+ resetTemplate: function () {
+ this.templateName = ''
+ this.templateUrl = ''
+ this.template = null
+ this.templateUploaded = false
+ },
+ resetFiles: function () {
+ this.files = []
+ this.filesDisplay = []
+ },
+ onTemplatePicked (e) {
+ this.resetTemplate()
+ const files = e.target.files
+ const file = {}
+ const f = files[0]
+ if (f !== undefined) {
+ this.templateName = f.name
+ file.fileName = this.templateName
+ const fr = new FileReader()
+ fr.readAsDataURL(f)
+ fr.addEventListener('load', () => {
+ this.templateUrl = fr.result
+ file.fileUrl = this.templateUrl
+ this.template = file
+ this.templateUploaded = true
+ })
+ } else {
+ this.resetTemplate()
+ }
+ },
+ onFilePicked (e) {
+ this.resetFiles()
+ const files = e.target.files
+ for (let i = 0; i < files.length; i++) {
+ const file = {}
+ const f = files[i]
+ if (f !== undefined) {
+ file.fileName = f.name
+ if (file.fileName.lastIndexOf('.') <= 0) {
+ return
+ }
+ const fr = new FileReader()
+ fr.readAsDataURL(f)
+ fr.addEventListener('load', () => {
+ file.fileUrl = fr.result
+ this.files.push(file)
+ this.filesDisplay.push(file)
+ })
+ }
+ }
+ },
+ successDlgClicked: function () {
+ this.$router.go(-1) // go back to previous page
+ },
+ submit: function () {
+ if (this.template != null) {
+ this.files.unshift(this.template)
+ } else {
+ this.renderDialog('Upload Error', 'Missing Template File')
+ return
+ }
+ this.setLoading()
+ const jm = new JobMgr()
+ const vm = this
+ jm.setJobType('xmlconv')
+ jm.setJobParameters({ datasetId: this.datasetSelected._id, templateName: this.templateName })
+ this.files.forEach(function (v) {
+ jm.addInputFile(v.fileName, v.fileUrl)
+ })
+ return jm.submitJob(function (jobId) {
+ vm.jobId = jobId
+ vm.resetLoading()
+ vm.renderDialog('Uploader Job Submitted Successfully', `Your uploader job is: ${jobId}
+ You should receive an email with a link to the job output.`, vm.successDlgClicked)
+ }, function (errCode, errMsg) {
+ vm.renderDialog('Upload Error', `Error submitting files for upload: errCode: ${errCode} msg: ${errMsg}`)
+ vm.resetLoading()
+ })
+ },
+ ...mapMutations({
+ toggleDialogBox: 'setDialogBox'
+ }),
+ renderDialog (title, content, closeHandler) {
+ this.dialog = {
+ title,
+ content,
+ closeHandler: closeHandler || this.toggleDialogBox
+ }
+ this.toggleDialogBox()
+ }
+ },
created () {
this.$store.commit('setAppHeaderInfo', { icon: 'cloud_upload', name: 'Data Uploader' })
}
diff --git a/app/src/router/module/nanomine.js b/app/src/router/module/nanomine.js
index ca54b26a..0807f412 100644
--- a/app/src/router/module/nanomine.js
+++ b/app/src/router/module/nanomine.js
@@ -6,7 +6,7 @@ const nanomineRoutes = [
meta: { requiresAuth: false }
},
{
- path: '/xml-uploader',
+ path: 'xml-uploader',
name: 'XmlUploader',
component: () => import(/* webpackChunkName: "xmlupload" */ '@/pages/nanomine/xmlUploader/XmlUploader.vue'),
meta: { requiresAuth: false }
diff --git a/app/src/store/modules/nanomine/referenceContainer/index.js b/app/src/store/modules/nanomine/referenceContainer/index.js
index d1d5769d..3733c2a3 100644
--- a/app/src/store/modules/nanomine/referenceContainer/index.js
+++ b/app/src/store/modules/nanomine/referenceContainer/index.js
@@ -100,6 +100,18 @@ export default {
title: 'Smilesdrawer: parsing and drawing SMILES-encoded molecular structures using client-side javascript.',
venue: 'Journal of chemical information and modeling 58.1 (2018): 1-7.',
date: '2018'
+ },
+ '10.1063/1.4943679': {
+ authors: 'Zhao, H., Li, X., Zhang, Y., Schadler, L. S., Chen, W., and Brinson, L. C.',
+ title: 'Perspective: NanoMine: A material genome approach for polymer nanocomposites analysis and design',
+ venue: 'APL Materials, 4(5), 053204',
+ date: '2016'
+ },
+ '10.1063/1.5046839': {
+ authors: 'Zhao, H., Wang, Y., Lin, A., Hu, B., Yan, R., McCusker, J., and Brinson, L. C.',
+ title: 'NanoMine schema: An extensible data representation for polymer nanocomposites',
+ venue: 'APL Materials, 6(11), 111108',
+ date: '2018'
}
}
}
diff --git a/app/tests/unit/components/nanomine/datasetviewer.spec.js b/app/tests/unit/components/nanomine/datasetviewer.spec.js
new file mode 100644
index 00000000..6a2528e8
--- /dev/null
+++ b/app/tests/unit/components/nanomine/datasetviewer.spec.js
@@ -0,0 +1,58 @@
+import createWrapper from '../../../jest/script/wrapper'
+import DatasetViewer from '@/components/nanomine/DatasetViewer.vue'
+
+var wrapper = null
+
+global.fetch = jest.fn()
+global.fetch.mockReturnValueOnce(
+ Promise.resolve({
+ data: {
+ data: [{ seq: 1, doi: 'testDOI1', title: 'testTitle1', datasetComment: 'testComment1', userID: '0' }]
+ }
+ }))
+ .mockReturnValueOnce(
+ Promise.resolve()
+ )
+ .mockReturnValueOnce(
+ Promise.resolve({
+ data: {
+ data: [{ seq: 1, doi: 'testDOI1', title: 'testTitle1', datasetComment: 'testComment1', userID: '0' },
+ { seq: 2, doi: 'testDOI2', title: 'testTitle2', datasetComment: 'testComment2', userID: '0' }]
+ }
+ })
+ )
+
+describe('DatasetCreateOrSelect.vue', () => {
+ beforeAll(() => {
+ wrapper = createWrapper(DatasetViewer, {})
+ })
+
+ it('mounts properly', () => {
+ expect(wrapper.exists()).toBeTruthy()
+ })
+
+ it('displays the provided dataset', () => {
+ expect(wrapper.text()).toContain('testDOI1')
+ expect(wrapper.text()).toContain('testTitle1')
+ expect(wrapper.text()).toContain('testComment1')
+ })
+
+ it('requests and displays a new dataset', async () => {
+ wrapper.vm.addDatasetSave()
+ await wrapper.vm.$nextTick() // one tick to get response, second to update list
+ await wrapper.vm.$nextTick()
+ expect(wrapper.text()).toContain('testDOI1')
+ expect(wrapper.text()).toContain('testTitle1')
+ expect(wrapper.text()).toContain('testComment1')
+ expect(wrapper.text()).toContain('testDOI2')
+ expect(wrapper.text()).toContain('testTitle2')
+ expect(wrapper.text()).toContain('testComment2')
+ })
+
+ it('returns search results', async () => {
+ wrapper.setData({ datasetSearch: 'testDOI2' })
+ await wrapper.vm.$nextTick()
+ expect(wrapper.text()).toContain('testDOI2')
+ expect(wrapper.text()).not.toContain('testDOI1')
+ })
+})
diff --git a/app/tests/unit/pages/nanomine/home.spec.js b/app/tests/unit/pages/nanomine/home.spec.js
index 6869ce52..ee0a1201 100644
--- a/app/tests/unit/pages/nanomine/home.spec.js
+++ b/app/tests/unit/pages/nanomine/home.spec.js
@@ -38,6 +38,6 @@ describe('Nanomine Homepage', () => {
const wrapper = await factory()
const link = wrapper.find('.quicklinks')
await link.trigger('click')
- expect(wrapper.vm.$route.path).toEqual('/xml-uploader')
+ expect(wrapper.vm.$route.path).toEqual('/nm/xml-uploader')
})
})