mirror of https://github.com/nocodb/nocodb
Pranav C
3 years ago
12 changed files with 537 additions and 119 deletions
@ -0,0 +1,90 @@ |
|||||||
|
import XLSX from 'xlsx' |
||||||
|
import TemplateGenerator from '~/components/import/TemplateGenerator' |
||||||
|
import { UITypes } from '~/components/project/spreadsheet/helpers/uiTypes' |
||||||
|
|
||||||
|
const excelTypeToUidt = { |
||||||
|
d: UITypes.DateTime, |
||||||
|
b: UITypes.Checkbox, |
||||||
|
n: UITypes.Number, |
||||||
|
s: UITypes.SingleLineText |
||||||
|
} |
||||||
|
|
||||||
|
export default class ExcelTemplateAdapter extends TemplateGenerator { |
||||||
|
constructor(name, ab) { |
||||||
|
super() |
||||||
|
this.name = name |
||||||
|
this.wb = XLSX.read(new Uint8Array(ab), { type: 'array' }) |
||||||
|
this.project = { |
||||||
|
title: this.name, |
||||||
|
tables: [] |
||||||
|
} |
||||||
|
this.data = {} |
||||||
|
} |
||||||
|
|
||||||
|
parse() { |
||||||
|
for (let i = 0; i < this.wb.SheetNames.length; i++) { |
||||||
|
const sheet = this.wb.SheetNames[i] |
||||||
|
const table = { tn: sheet, columns: [] } |
||||||
|
this.data[sheet] = [] |
||||||
|
const ws = this.wb.Sheets[sheet] |
||||||
|
const rows = XLSX.utils.sheet_to_json(ws, { header: 1 }) |
||||||
|
|
||||||
|
for (let col = 0; col < rows[0].length; col++) { |
||||||
|
const column = { |
||||||
|
cn: rows[0][col] |
||||||
|
} |
||||||
|
|
||||||
|
const cellProps = ws[`${col.toString(26).split('').map(s => (parseInt(s, 26) + 10).toString(36).toUpperCase())}2`] |
||||||
|
|
||||||
|
column.uidt = excelTypeToUidt[cellProps.t] || UITypes.SingleLineText |
||||||
|
|
||||||
|
// todo: optimize
|
||||||
|
if (column.uidt === UITypes.SingleLineText) { |
||||||
|
// check for long text
|
||||||
|
if (rows.some(r => (r[col] || '').toString().length > 255)) { |
||||||
|
column.uidt = UITypes.LongText |
||||||
|
} else { |
||||||
|
const vals = rows.slice(1).map(r => r[col]).filter(v => v !== null && v !== undefined) |
||||||
|
|
||||||
|
// check column is multi or single select by comparing unique values
|
||||||
|
if (vals.some(v => v && v.includes(','))) { |
||||||
|
const flattenedVals = vals.flatMap(v => v ? v.split(',') : []) |
||||||
|
const uniqueVals = new Set(flattenedVals) |
||||||
|
if (flattenedVals.length > uniqueVals.size && uniqueVals.size <= flattenedVals.length / 10) { |
||||||
|
column.uidt = UITypes.MultiSelect |
||||||
|
column.dtxp = [...uniqueVals].join(',') |
||||||
|
} |
||||||
|
} else { |
||||||
|
const uniqueVals = new Set(vals) |
||||||
|
if (vals.length > uniqueVals.size && uniqueVals.size <= vals.length / 10) { |
||||||
|
column.uidt = UITypes.SingleSelect |
||||||
|
column.dtxp = [...uniqueVals].join(',') |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
table.columns.push(column) |
||||||
|
} |
||||||
|
|
||||||
|
for (const row of rows.slice(1)) { |
||||||
|
const rowData = {} |
||||||
|
for (let i = 0; i < table.columns.length; i++) { |
||||||
|
// toto: do parsing if necessary based on type
|
||||||
|
rowData[table.columns[i].cn] = row[i] |
||||||
|
} |
||||||
|
this.data[sheet].push(rowData) |
||||||
|
} |
||||||
|
|
||||||
|
this.project.tables.push(table) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
getTemplate() { |
||||||
|
return this.project |
||||||
|
} |
||||||
|
|
||||||
|
getData() { |
||||||
|
return this.data |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
export default class TemplateGenerator { |
||||||
|
parse() { |
||||||
|
throw new Error('\'parse\' method is not implemented') |
||||||
|
} |
||||||
|
|
||||||
|
getTemplate() { |
||||||
|
throw new Error('\'getTemplate\' method is not implemented') |
||||||
|
} |
||||||
|
|
||||||
|
getData() { |
||||||
|
throw new Error('\'getData\' method is not implemented') |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,148 @@ |
|||||||
|
<template> |
||||||
|
<div> |
||||||
|
<v-menu bottom offset-y> |
||||||
|
<template #activator="{on}"> |
||||||
|
<v-btn |
||||||
|
:loading="projectCreation" |
||||||
|
:disabled="projectCreation" |
||||||
|
class="primary" |
||||||
|
x-large |
||||||
|
v-on="on" |
||||||
|
> |
||||||
|
Use template |
||||||
|
<v-icon>mdi-menu-down</v-icon> |
||||||
|
</v-btn> |
||||||
|
</template> |
||||||
|
<v-list> |
||||||
|
<v-list-item dense class="py-2" @click="useTemplate('rest')"> |
||||||
|
<v-list-item-title> |
||||||
|
<v-icon class="mr-1" :color="textColors[7]"> |
||||||
|
mdi-code-json |
||||||
|
</v-icon> |
||||||
|
Create REST Project |
||||||
|
</v-list-item-title> |
||||||
|
</v-list-item> |
||||||
|
<v-list-item dense class="py-2" @click="useTemplate('graphql')"> |
||||||
|
<v-list-item-title> |
||||||
|
<v-icon class="mr-1" :color="textColors[3]"> |
||||||
|
mdi-graphql |
||||||
|
</v-icon> |
||||||
|
Create GQL Project |
||||||
|
</v-list-item-title> |
||||||
|
</v-list-item> |
||||||
|
</v-list> |
||||||
|
</v-menu> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import colors from '~/mixins/colors' |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'CreateProjectFromTemplateBtn', |
||||||
|
mixins: [colors], |
||||||
|
props: { |
||||||
|
loading: Boolean, |
||||||
|
templateData: Object, |
||||||
|
importData: Object, |
||||||
|
loaderMessage: String |
||||||
|
}, |
||||||
|
data() { |
||||||
|
return { |
||||||
|
projectCreation: false, |
||||||
|
loaderMessagesIndex: 0, |
||||||
|
loaderMessages: [ |
||||||
|
'Setting up new database configs', |
||||||
|
'Inferring database schema', |
||||||
|
'Generating APIs.', |
||||||
|
'Generating APIs..', |
||||||
|
'Generating APIs...', |
||||||
|
'Generating APIs....', |
||||||
|
'Please wait', |
||||||
|
'Please wait.', |
||||||
|
'Please wait..', |
||||||
|
'Please wait...', |
||||||
|
'Please wait..', |
||||||
|
'Please wait.', |
||||||
|
'Please wait', |
||||||
|
'Please wait.', |
||||||
|
'Please wait..', |
||||||
|
'Please wait...', |
||||||
|
'Please wait..', |
||||||
|
'Please wait.', |
||||||
|
'Please wait..', |
||||||
|
'Please wait...' |
||||||
|
] |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async useTemplate(projectType) { |
||||||
|
// this.$emit('useTemplate', type) |
||||||
|
|
||||||
|
this.projectCreation = true |
||||||
|
try { |
||||||
|
const interv = setInterval(() => { |
||||||
|
debugger |
||||||
|
this.loaderMessagesIndex = this.loaderMessagesIndex < this.loaderMessages.length - 1 ? this.loaderMessagesIndex + 1 : 6 |
||||||
|
this.$emit('update:loaderMessage', this.loaderMessages[this.loaderMessagesIndex]) |
||||||
|
}, 1000) |
||||||
|
|
||||||
|
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'projectCreateByWebWithXCDB', { |
||||||
|
title: this.templateData.title, |
||||||
|
projectType, |
||||||
|
template: this.templateData |
||||||
|
}]) |
||||||
|
|
||||||
|
await this.$store.dispatch('project/ActLoadProjectInfo') |
||||||
|
|
||||||
|
clearInterval(interv) |
||||||
|
if (this.importData) { |
||||||
|
this.$emit('update:loaderMessage', 'Importing excel data to project') |
||||||
|
await this.importDataToProject({ projectId: result.id, projectType, prefix: result.prefix }) |
||||||
|
} |
||||||
|
|
||||||
|
this.$emit('update:loaderMessage', null) |
||||||
|
|
||||||
|
this.projectReloading = false |
||||||
|
|
||||||
|
this.$router.push({ |
||||||
|
path: `/nc/${result.id}`, |
||||||
|
query: { |
||||||
|
new: 1 |
||||||
|
} |
||||||
|
}) |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
this.$toast.error(e.message).goAway(3000) |
||||||
|
} |
||||||
|
this.projectCreation = false |
||||||
|
}, |
||||||
|
async importDataToProject({ projectId, projectType, prefix = '' }) { |
||||||
|
this.$store.commit('project/MutProjectId', projectId) |
||||||
|
this.$ncApis.setProjectId(projectId) |
||||||
|
|
||||||
|
await Promise.all(Object.entries(this.importData).map(async([table, data]) => { |
||||||
|
await this.$store.dispatch('meta/ActLoadMeta', { |
||||||
|
tn: `${prefix}${table}` |
||||||
|
}) |
||||||
|
|
||||||
|
// todo: get table name properly |
||||||
|
const api = this.$ncApis.get({ |
||||||
|
table: `${prefix}${table}`, |
||||||
|
type: projectType |
||||||
|
}) |
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i += 500) { |
||||||
|
console.log(data[i]) |
||||||
|
await api.insertBulk(data.slice(i, i + 500)) |
||||||
|
} |
||||||
|
})) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
|
||||||
|
</style> |
Loading…
Reference in new issue