Add Items from Thing: Expert Mode (#603)

This adds a new "expert mode" to the
"Create Equipment from Thing"/"Add Equipment to Model"/
"Create Points from Thing"/"Add Points to Model"

Instead of clicking through the channels and
filling out items, a textual definition will be
generated and put it the "Add Items from
Textual Definition" page. That way, the expert
user can add their items by editing text with the
well-known syntax.

Signed-off-by: Yannick Schaus <github@schaus.net>
pull/606/merge
Yannick Schaus 2020-12-08 17:40:25 +01:00 committed by GitHub
parent e26eac6656
commit 6f81c0a6c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 3 deletions

View File

@ -93,7 +93,7 @@ import ChannelGroup from './channel-group.vue'
import ChannelLink from './channel-link.vue'
import ItemForm from '@/components/item/item-form.vue'
import Points from '@/assets/semantics'
import { Points } from '@/assets/semantics'
export default {
props: ['thingType', 'thing', 'channelTypes', 'pickerMode', 'multipleLinksMode', 'itemTypeFilter', 'newItemsPrefix', 'newItems', 'context'],

View File

@ -112,9 +112,10 @@ export default {
components: {
'editor': () => import('@/components/config/controls/script-editor.vue')
},
props: ['textualDefinition'],
data () {
return {
itemsDsl: '',
itemsDsl: this.textualDefinition || '',
items: [],
things: [],
links: [],

View File

@ -62,6 +62,7 @@
You can alter the suggested names and labels as well as the semantic class and related property.<br/><br/>
The newly created Points will be linked to their respective channels with the default profile
(you will be able to configure the links individually later if needed).
<f7-link class="display-block margin-top-half" @click="switchToExpertMode" color="blue">Expert Mode</f7-link>
</f7-block-footer>
<channel-list :thing="selectedThing" :thingType="selectedThingType" :channelTypes="selectedThingChannelTypes"
:multiple-links-mode="true" :new-items-prefix="(createEquipment) ? newEquipmentItem.name : (parentGroup) ? parentGroup.name : ''"
@ -91,6 +92,8 @@ import Item from '@/components/item/item.vue'
import ThingStatus from '@/components/thing/thing-status-mixin'
import generateTextualDefinition from './generate-textual-definition'
export default {
mixins: [ThingStatus],
components: {
@ -120,8 +123,30 @@ export default {
this.selectedThingId = this.thingId
}
},
toggleSelect (channel) {
switchToExpertMode () {
try {
let parentGroupsForEquipment, parentGroupsForPoints
if (this.createEquipment) {
parentGroupsForEquipment = (this.parentGroup) ? [this.parentGroup.name] : []
parentGroupsForPoints = [this.newEquipmentItem.name]
} else {
parentGroupsForEquipment = []
parentGroupsForPoints = (this.parent) ? [this.parent.item.name] : (this.parentGroup) ? [this.parentGroup.name] : []
}
const itemsDefinition = generateTextualDefinition(this.selectedThing, this.selectedThingChannelTypes, (this.createEquipment) ? this.newEquipmentItem : null, parentGroupsForEquipment, parentGroupsForPoints)
this.$f7router.navigate('/settings/items/add-from-textual-definition', {
props: {
textualDefinition: itemsDefinition
},
pushState: false,
reloadCurrent: true
})
} catch (e) {
console.error(e)
this.$f7.dialog.alert('There was an error generating the items definition: ' + e)
}
},
add () {
if (!this.selectedThingId) {

View File

@ -0,0 +1,74 @@
import diacritic from 'diacritic'
import { Points } from '@/assets/semantics'
/**
* Generate a textual definition for the items provided by the "add from thing" page,
* for expert users who prefer to edit their items that way
*/
export default (thing, channelTypes, newEquipmentItem, parentGroupsForEquipment, parentGroupsForPoints) => {
const channelTypesMap = new Map(channelTypes.map(ct => [ct.UID, ct]))
let def = ''
if (newEquipmentItem && newEquipmentItem.name) {
def += `// Equipment representing thing:\n// ${thing.UID}\n`
def += `// (${thing.label})\n\n`
def += `Group ${newEquipmentItem.name} "${newEquipmentItem.label}" `
if (newEquipmentItem.category) def += `<${newEquipmentItem.category}> `
if (parentGroupsForEquipment.length) def += `(${parentGroupsForEquipment.join(', ')}) `
if (newEquipmentItem.tags.length) def += `[${newEquipmentItem.tags.map((t) => `"${t}"`).join(', ')}] `
def = def.trim() + '\n\n'
}
let lines = []
for (const channel of thing.channels) {
if (channel.kind !== 'STATE') continue
const channelType = channelTypesMap.get(channel.channelTypeUID)
let newItemName = (newEquipmentItem) ? newEquipmentItem.name : diacritic.clean(thing.label).replace(/[^0-9a-z]/gi, '')
newItemName += '_'
let suffix = channel.label || channel.id
if (thing.channels.filter((c) => c.label === suffix).length > 1) {
suffix = channel.id.replace('#', '_')
}
newItemName += diacritic.clean(suffix).replace(/[^0-9a-z_]/gi, '')
const defaultTags = (channel.defaultTags.length > 0) ? channel.defaultTags : channelType.tags
const newItem = {
channel: channel,
channelType: channelType,
name: newItemName,
label: channel.label || channelType.label,
groupNames: parentGroupsForPoints,
category: (channelType) ? channelType.category : '',
type: channel.itemType,
tags: (defaultTags.find((t) => Points.indexOf(t) >= 0)) ? defaultTags : [...defaultTags, 'Point']
}
let line = []
line.push(newItem.type)
line.push(newItem.name)
line.push(`"${newItem.label}"`)
if (channelType.advanced) line[0] = '// ' + line[0] // comment the advanced channels by default
line.push((newItem.category) ? `<${newItem.category}>` : '')
line.push((newItem.groupNames.length) ? `(${newItem.groupNames.join(', ')})` : '')
line.push((newItem.tags.length) ? `[${newItem.tags.map((t) => `"${t}"`).join(', ')}] ` : '')
line.push(`{ channel="${channel.uid}" }`)
lines.push(line)
}
if (!lines.length) return def
let columnsWidths = []
for (let c = 0; c < lines[0].length; c++) {
columnsWidths.push(Math.max(...lines.map((l) => l[c].length)) + 1)
}
def += '// Points:\n\n'
lines.forEach((l) => {
for (let c = 0; c < l.length; c++) {
def += l[c] + ' '.repeat(columnsWidths[c] - l[c].length)
}
def += '\n'
})
return def
}