You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
6.9 KiB

3 years ago
3 years ago
3 years ago
  1. <template>
  2. <v-card>
  3. <v-card-title>
  4. {{ $t('dataset.importDataTitle') }}
  5. </v-card-title>
  6. <v-card-text>
  7. <v-overlay :value="isImporting">
  8. <v-progress-circular
  9. indeterminate
  10. size="64"
  11. />
  12. </v-overlay>
  13. <v-select
  14. v-model="selected"
  15. :items="catalog"
  16. item-text="name"
  17. label="File format"
  18. outlined
  19. />
  20. <v-form v-model="valid">
  21. <v-text-field
  22. v-for="(item, key) in textFields"
  23. :key="key"
  24. v-model="option[key]"
  25. :label="item.title"
  26. :rules="requiredRules"
  27. outlined
  28. />
  29. <v-select
  30. v-for="(val, key) in selectFields"
  31. :key="key"
  32. v-model="option[key]"
  33. :items="val.enum"
  34. :label="val.title"
  35. outlined
  36. >
  37. <template #selection="{ item }">
  38. {{ toVisualize(item) }}
  39. </template>
  40. <template #item="{ item }">
  41. {{ toVisualize(item) }}
  42. </template>
  43. </v-select>
  44. </v-form>
  45. <v-sheet
  46. v-if="selected"
  47. :dark="!$vuetify.theme.dark"
  48. :light="$vuetify.theme.dark"
  49. class="mb-5 pa-5"
  50. >
  51. <pre>{{ example }}</pre>
  52. </v-sheet>
  53. <file-pond
  54. v-if="selected && acceptedFileTypes !== '*'"
  55. ref="pond"
  56. chunk-uploads="true"
  57. label-idle="Drop files here..."
  58. :allow-multiple="true"
  59. :accepted-file-types="acceptedFileTypes"
  60. :server="server"
  61. :files="myFiles"
  62. @processfile="handleFilePondProcessfile"
  63. @removefile="handleFilePondRemovefile"
  64. />
  65. <file-pond
  66. v-if="selected && acceptedFileTypes === '*'"
  67. ref="pond"
  68. chunk-uploads="true"
  69. label-idle="Drop files here..."
  70. :allow-multiple="true"
  71. :server="server"
  72. :files="myFiles"
  73. @processfile="handleFilePondProcessfile"
  74. @removefile="handleFilePondRemovefile"
  75. />
  76. <v-data-table
  77. v-if="errors.length > 0"
  78. :headers="headers"
  79. :items="errors"
  80. class="elevation-1"
  81. ></v-data-table>
  82. </v-card-text>
  83. <v-card-actions>
  84. <v-spacer />
  85. <v-btn
  86. class='text-capitalize me-2 primary'
  87. :disabled="isDisabled"
  88. @click="importDataset"
  89. >
  90. Import
  91. </v-btn>
  92. </v-card-actions>
  93. </v-card>
  94. </template>
  95. <script>
  96. import Cookies from 'js-cookie'
  97. import vueFilePond from "vue-filepond"
  98. import "filepond/dist/filepond.min.css"
  99. import FilePondPluginFileValidateType from "filepond-plugin-file-validate-type"
  100. const FilePond = vueFilePond(
  101. FilePondPluginFileValidateType,
  102. )
  103. export default {
  104. components: {
  105. FilePond,
  106. },
  107. layout: 'project',
  108. data() {
  109. return {
  110. catalog: [],
  111. selected: null,
  112. myFiles: [],
  113. option: {'column_data': '', 'column_label': '', 'delimiter': ''},
  114. taskId: null,
  115. polling: null,
  116. errors: [],
  117. headers: [
  118. { text: 'Filename', value: 'filename' },
  119. { text: 'Line', value: 'line' },
  120. { text: 'Message', value: 'message' }
  121. ],
  122. requiredRules: [
  123. v => !!v || 'Field value is required'
  124. ],
  125. server: {
  126. url: '/v1/fp',
  127. headers: {
  128. 'X-CSRFToken': Cookies.get('csrftoken'),
  129. },
  130. process: {
  131. url: '/process/',
  132. method: 'POST',
  133. },
  134. patch: '/patch/',
  135. revert: '/revert/',
  136. restore: '/restore/',
  137. load: '/load/',
  138. fetch: '/fetch/'
  139. },
  140. uploadedFiles: [],
  141. valid: false,
  142. isImporting: false,
  143. }
  144. },
  145. computed: {
  146. isDisabled() {
  147. return this.uploadedFiles.length === 0 || this.taskId !== null || !this.valid
  148. },
  149. properties() {
  150. const item = this.catalog.find(item => item.name === this.selected)
  151. if (item) {
  152. return item.properties
  153. } else {
  154. return {}
  155. }
  156. },
  157. textFields() {
  158. const asArray = Object.entries(this.properties)
  159. const textFields = asArray.filter(([key, value]) => !('enum' in value))
  160. return Object.fromEntries(textFields)
  161. },
  162. selectFields() {
  163. const asArray = Object.entries(this.properties)
  164. const textFields = asArray.filter(([key, value]) => 'enum' in value)
  165. return Object.fromEntries(textFields)
  166. },
  167. acceptedFileTypes() {
  168. const item = this.catalog.find(item => item.name === this.selected)
  169. if (item) {
  170. return item.acceptTypes
  171. } else {
  172. return ''
  173. }
  174. },
  175. example() {
  176. const item = this.catalog.find(item => item.name === this.selected)
  177. if (item) {
  178. const column_data = 'column_data'
  179. const column_label = 'column_label'
  180. if (column_data in this.option && column_label in this.option) {
  181. return item.example.replaceAll(column_data, this.option[column_data])
  182. .replaceAll(column_label, this.option[column_label])
  183. .trim()
  184. } else {
  185. return item.example.trim()
  186. }
  187. } else {
  188. return ''
  189. }
  190. }
  191. },
  192. watch: {
  193. selected() {
  194. const item = this.catalog.find(item => item.name === this.selected)
  195. for (const [key, value] of Object.entries(item.properties)) {
  196. this.option[key] = value.default
  197. }
  198. this.myFiles = []
  199. for (const file of this.uploadedFiles) {
  200. this.$services.parse.revert(file.serverId)
  201. }
  202. this.uploadedFiles = []
  203. this.errors = []
  204. }
  205. },
  206. async created() {
  207. this.catalog = await this.$services.catalog.list(this.$route.params.id)
  208. this.pollData()
  209. },
  210. beforeDestroy() {
  211. clearInterval(this.polling)
  212. },
  213. methods: {
  214. handleFilePondProcessfile(error, file) {
  215. console.log(error)
  216. this.uploadedFiles.push(file)
  217. this.$nextTick()
  218. },
  219. handleFilePondRemovefile(error, file) {
  220. console.log(error)
  221. const index = this.uploadedFiles.findIndex(item => item.id === file.id)
  222. if (index > -1) {
  223. this.uploadedFiles.splice(index, 1)
  224. this.$nextTick()
  225. }
  226. },
  227. async importDataset() {
  228. this.isImporting = true
  229. this.taskId = await this.$services.parse.analyze(
  230. this.$route.params.id,
  231. this.selected,
  232. this.uploadedFiles.map(item => item.serverId),
  233. this.option
  234. )
  235. },
  236. pollData() {
  237. this.polling = setInterval(async() => {
  238. if (this.taskId) {
  239. const res = await this.$services.taskStatus.get(this.taskId)
  240. if (res.ready) {
  241. this.taskId = null
  242. this.errors = res.result.error
  243. this.myFiles = []
  244. this.uploadedFiles = []
  245. this.isImporting = false
  246. }
  247. }
  248. }, 3000)
  249. },
  250. toVisualize(text) {
  251. if (text === '\t') {
  252. return 'Tab'
  253. } else if (text === ' ') {
  254. return 'Space'
  255. } else if (text === '') {
  256. return 'None'
  257. } else {
  258. return text
  259. }
  260. }
  261. },
  262. };
  263. </script>