For a project I'm working on we get some data delivered in an Excel sheet which I convert to CSV through Excel.
These files contain measurements with different categories but the same ID.
Example
readingId; category; result;
1 ; cat 1 ; A
1 ; cat 2 ; B
2 ; cat1 ; C
I've then converted the CSV to JSON and wrote a function to output the data into different objects
const fs = require('fs');
const path = require('path');
exports.convertJson = (file) => {
let rawData = fs.readFileSync(file);
let jsonData = JSON.parse(rawData);
let rawOutput = [];
for (output of jsonData) {
rawOutput.push({
locationId: output.Meetlocatienummer,
date: output.Aanmaakdatum_score,
subCategorie: output.Bestekspost,
score: output.Score,
scoreNumber: output.Cijfer,
categories: output.Categorie,
coordinates: output.Coordinaten,
neighbourhoodIndex: output.BUURTCODE,
quality: output.KWALITEIT,
district: output.STADSDEEL,
distrcitIndex: output.STADSDLCD,
street: output.STRAATNAAM,
neighbourhood: output.WIJK,
cluster: output.Cluster,
});
}
return rawOutput;
};
Which outputs the following results
[
{
locationId: 10215,
date: undefined,
subCategorie: 'Meubilair-afvalbak-vullingsgraad',
score: '',
scoreNumber: 8,
categories: 'Meubilair',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Meubilair-container-bijgeplaatst afval rondom container',
score: 'A+',
scoreNumber: 10,
categories: 'Meubilair',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Riolering-kolk-belemmering inlaat',
score: 'A+',
scoreNumber: 10,
categories: 'Riolering',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Verharding-open verharding-elementenverharding-onkruid',
score: 'A',
scoreNumber: 8,
categories: 'Verharding',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Verharding-natuurlijk afval',
score: 'A',
scoreNumber: 8,
categories: 'Verharding',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Verharding-uitwerpselen',
score: 'A+',
scoreNumber: 10,
categories: 'Verharding',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Verharding-zwerfafval grof',
score: 'A',
scoreNumber: 8,
categories: 'Verharding',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Verharding-veegvuil goten',
score: 'A',
scoreNumber: 8,
categories: 'Verharding',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Verharding-onkruid rondom obstakels',
score: 'B',
scoreNumber: 6,
categories: 'Verharding',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Verharding-grof vuil',
score: 'A+',
scoreNumber: 10,
categories: 'Verharding',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 10215,
date: undefined,
subCategorie: 'Verharding-zwerfafval fijn',
score: 'A',
scoreNumber: 8,
categories: 'Verharding',
coordinates: '52.072843, 4.287723',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Xaverystraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
},
{
locationId: 7466,
date: undefined,
subCategorie: 'Meubilair-afvalbak-vullingsgraad',
score: 'B',
scoreNumber: 6,
categories: 'Meubilair',
coordinates: '52.072647, 4.288656',
neighbourhoodIndex: 10,
quality: 'residentiekwaliteit',
district: 'Segbroek',
distrcitIndex: 3,
street: 'Jan Krosstraat',
neighbourhood: 'Regentessekwartier',
cluster: 'WRF'
}
]
In the end I would like to write this information to MongoDB and I had the following scheme in mind to reduce the loads of duplicated data
{
locationId: output.Meetlocatienummer,
date: output.Aanmaakdatum_score,
subCategories: [
{
subCategory: output.Bestekspost,
score: output.Score,
scoreNumber: output.Cijfer,
},
],
categories: [{ category: output.Categorie }],
coordinates: output.Coordinaten,
neighbourhoodIndex: output.BUURTCODE,
quality: output.KWALITEIT,
district: output.STADSDEEL,
distrcitIndex: output.STADSDLCD,
street: output.STRAATNAAM,
neighbourhood: output.WIJK,
cluster: output.Cluster,
}
This project is a hobby project while learning NodeJS. The actual data are readings how much the streets of the city I work for a poluted with litter. It's a bit boring to read thousands of lines in Excel to find the hotspots of the city as it's a bit boring to just read some scores and graphs so I though it would be nice to import it into Leaflet through NodeJS.
The actual backend will contain more functionality as I learn Node and maybe in the future React, that's why I try to write it myself rather then importing the data into Google maps, which works oke but lacks detailed category filtering.
I hope my idea is a bit clear and someone can point me in the right direction.
Edit 1
I got a bit further with lodash.
return _(rawOutput)
.groupBy('locationId')
.map((obj) => _.assignWith({}, ...obj, (val1, val2) => val1 || val2))
.value();
I found the above snippet and now I only get 1 output per unique locationId but now I'm stuck with contructing the final output with the subcategories.
I was also playing around a bit with csv-parser to directly go from the csv to a proper json output, which would be ideal because I don't have to convert it manually then.
I'll get back to it tomorrow :-)
If you take that JSON and mongoimport into MongoDB, you can use the following pipeline to transform it -- although honestly, a little python script on the outside could construct the structure just as easily and then you would still import the condensed data.
db.foo.aggregate([
{$group: {_id: "$locationId",
subCategories: {$push: {subCategory: "$subCategorie", score:"$score", scoreNumber: "$scoreNumber"}},
categories: {$push: "$categories"},
// Just take the first occurance of each of these since they are claimed
// to be the same.
date: {$first: "$date"},
neighbourhoodIndex: {$first: "$neighbourhoodIndex"},
quality: {$first: "$quality"},
district: {$first: "$district"},
distrcitIndex: {$first: "$distrcitIndex"},
street: {$first: "$street"},
neighbourhood: {$first: "$neighbourhood"},
cluster: {$first: "$cluster"},
coordinates: {$first: "$coordinates"}
}}
// Now that we have a single doc with locationId x and a coordinate, convert
// the string lat,long "52.072843, 4.287723" into a GeoJSON Point which is
// a long,lat array of doubles. We convert by using $addFields to
// overwrite the original coordinates field:
,{$addFields: {"coordinates": {$let: {
vars: {pt: {$split:["$coordinates",","]}},
in: {"type": "Point", "coordinates": [
{$toDouble: {$trim: {input:{$arrayElemAt:["$$pt",1]}}}},
{$toDouble: {$trim: {input:{$arrayElemAt:["$$pt",0]}}}}
]
}
}}
}}
// Put the whole transformed thing into a new collection named "foo2":
,{$out: "foo2"}
]);
Alright, in the end the code from Buzz Moschetti was exactly what I wanted to get rid of the duplicated data. I didn't hear about aggregates yet so thanks for that.
I ended up using the CSV Parse library to convert the CSV to JSON, drop that into the database and then query out the duplicates with the code from Buzz.
I haven't written the code yet to write back the cleaned up data back to the database but that shouldn't be to hard so I'll just post what I have now for as reference for others.
First of all I have written a csv helper for the conversion.
const fs = require('fs');
const { parse } = require('csv');
const moment = require('moment');
exports.processFile = async (filePath) => {
const records = [];
const input = fs.createReadStream(filePath);
const parser = parse({
// CSV options
bom: true,
delimiter: ';',
cast: (value, context) => {
if (context.header) return value;
// Convert data
if (context.column === 'date') {
const dateString = moment(value, 'dd-mm-yyyy h:mm');
const date = dateString.toDate();
return date;
}
// Convert coordinates to GeoJSON
if (context.column === 'coordinates') {
const coordinate = value.split(',');
const geoData = {
type: 'Point',
coordinate: [coordinate[0], coordinate[1]],
};
return geoData;
}
// Output rest of the fields
return String(value);
},
columns: [
'locationId', // meetlocatienummer
'date', // aanmaakdatum score
'subCategory', //bestekpost
'category', // categorie
'score', // score
'coordinates', //coordinaten
undefined, // buurt
undefined, // buurtcode
undefined, // gebied
undefined, // id
'quality', //kwaliteit
undefined, // stadsdeel
'districtIndex', //stadsdlcd
'street', //straatnaam
undefined, //vaknr
'neighbourhood', //wijk
undefined, //wijkcode
'cluster', //cluster
'scoreNumber', //cijfer
undefined, // week
undefined, // maand
undefined, // jr-mnd
undefined, // jaar
],
trim: true,
from_line: 2,
skip_records_with_empty_values: true,
});
// parser.on('error', (err) => {
// console.log(err);
// const error = new Error(err);
// error.httpStatusCode = 500;
// throw error;
// });
//const transformer = transform((record, callback) => {});
input.pipe(parser).on('error', (err) => {
input.close();
});
for await (let record of parser) {
// Skip all lines without coordinates
if (record.coordinates.coordinate[1] === undefined) {
continue;
}
// Push filename to the record object
record.fileName = filePath;
// Push records for final output
records.push(record);
//console.log('Records converted');
}
return records;
};
I'm uploading the file with the Multer lirbary. Here's the POST action in my import data controller. After the file has been uploaded the conversion starts. If an error occurs the file gets deleted again and no records are written to the database. If the conversion succeeds the records will be written to importdatas in MongoDB, these are still the 'dirty' records, so loads of duplicates but without useless data which gets filtered by the CSV Parse helper. (Basically all the data without coordinates)
exports.postImportData = (req, res, next) => {
const uploadedCSV = req.file;
//console.log(uploadedCSV);
// Load imported CSV files from DB to be able to delete them
ImportedCSVFile.find()
.sort({ date: -1 })
.then((result) => {
// Check if there are already files imported
let hasFiles = null;
if (result.length > 0) {
hasFiles = 1;
}
// If there are any erros with the file being uploaded
if (req.fileValidationError) {
return res.render('admin/import-data/import-data', {
pageTitle: 'Importeer data',
path: '/admin/import-data',
files: result,
activeAdmin: true,
errorMessage: req.fileValidationError,
validationErrors: [],
hasFiles,
});
}
// If there's no file uploaded
if (!uploadedCSV) {
return res.render('admin/import-data/import-data', {
pageTitle: 'Importeer data',
path: '/admin/import-data',
files: result,
activeAdmin: true,
errorMessage: 'Geen bestand geselecteerd',
validationErrors: [],
hasFiles,
});
}
(async () => {
const csvFile = await fileHelper.hasFile(uploadedCSV);
try {
const records = await convert.processFile(csvFile);
// Write all CSV data to importdatas in MongoDB
await ImportData.insertMany(records)
.then((result) => {
console.log('Data imported');
// Push info about the uploaded file into 'importedcsvfiles'
const importedCSVFile = new ImportedCSVFile({
filePath: fileHelper.hasFile(uploadedCSV),
originalName: uploadedCSV.originalname,
});
return (
importedCSVFile
.save() // Save all CSV data into 'importedcsvfiles' in MongoDB
.then((result) => {
res.redirect('/admin/import-data');
})
// Catch save filepath error
.catch((err) => {
console.log('save failed');
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
})
);
})
// Catch insert CSV data into DB error
.catch((err) => {
console.log('insert many failed');
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
} catch (err) {
// console.log(error);
fileHelper.removeFile(csvFile);
return res.render('admin/import-data/import-data', {
pageTitle: 'Importeer data',
path: '/admin/import-data',
files: result,
activeAdmin: true,
errorMessage:
'Het geselecteerde bestand heeft niet de juiste indeling. Neem contact op met de beheerder.',
validationErrors: [],
hasFiles,
});
}
})();
});
};
Also wrote a delete option which removes the CSV file and all the database records which are linked to that file
exports.postDeleteData = (req, res, next) => {
const dataId = req.body.dataId;
ImportedCSVFile.findById(dataId)
.then((result) => {
// console.log('FilePath:');
// console.log(result.filePath);
const filePath = result.filePath;
const deleteData = async () => {
await ImportData.deleteMany({ filePath: filePath })
.then((result) => {})
.catch((err) => {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
await ImportedCSVFile.findByIdAndDelete(dataId)
.then((result) => {
console.log('Data deleted');
fileHelper.removeFile(filePath);
res.redirect('/admin/import-data');
})
.catch((err) => {
console.log('here');
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
};
return deleteData();
})
.catch((err) => {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
};
And for now the Aggregate code from Buzz to clean up the data and drop it into Leaflet so I get 1 point with all the different categories.
const { ImportData, OutputData } = require('../models/importData.model');
// Main controller for the homepage
exports.getMainController = (req, res, next) => {
ImportData.aggregate([
{
$group: {
_id: '$coordinates',
subCategories: {
$push: {
subCategory: '$subCategory',
score: '$score',
scoreNumber: '$scoreNumber',
},
},
categories: { $push: '$category' },
// Just take the first occurance of each of these since they are claimed
// to be the same.
date: { $first: '$date' },
quality: { $first: '$quality' },
districtIndex: { $first: '$districtIndex' },
street: { $first: '$street' },
neighbourhood: { $first: '$neighbourhood' },
cluster: { $first: '$cluster' },
},
},
]).exec((err, locations) => {
if (err) {
throw next(err);
}
//console.log(locations);
res.render('index.ejs', {
pageTitle: 'Kaart',
path: '/kaart',
activeAdmin: true,
data: locations,
errorMessage: null,
});
});
};
As of now I just query this data like I said in the beginning. As I am still learning a lot about Javascript and Node I now started to build a frontend with React. Once I got on going there I will convert all this code to an API and I'll finish this part of the project.
Hello All and thank you in advance for your help :)
Can someone help me understand how I can take the below code, which displays data for a specified playlist, and have it only show the artist and track names? I have been toying around with the API documentation for several hours and I have not been able to make heads or tales of it. Right now when it displays data it gives me a whole bunch of data in a jumbled mess. Also, note that I put dummy values in the client_id and Secret parts of this code.
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
import json
PlaylistExample = '37i9dQZEVXbMDoHDwVN2tF'
cid = '123'
secret = 'xyz'
auth_manager = SpotifyClientCredentials(client_id=cid, client_secret=secret)
sp = spotipy.Spotify(auth_manager=auth_manager)
playlist_id = 'spotify:user:spotifycharts:playlist:37i9dQZEVXbJiZcmkrIHGU'
results = sp.playlist(playlist_id)
print(json.dumps(results, indent=4))
Would something like this be useful?:
print("Song - Artist - Album\n")
for item in results['tracks']['items']:
print(
item['track']['name'] + ' - ' +
item['track']['artists'][0]['name'] + ' - ' +
item['track']['album']['name']
)
Your output will look similar to this:
Song - Artist - Album
ONLY - ZHU - ONLY
Bad - 2012 Remaster - Michael Jackson - Bad 25th Anniversary
Orion - Rodrigo y Gabriela - Rodrigo y Gabriela
Shape of You - Ed Sheeran - ÷ (Deluxe)
Alternatively, you could create your own structure based on the returned one by Spotify but just keeping what you need:
result_dict = {
'tracks': {
'items': [],
'limit': 100,
'next': None,
'offset': 0,
'previous': None,
'total': 16
},
'type': 'playlist',
'uri': '<playlist_uri>'
}
And your track structure that goes inside 'items' from above:
track_dict = {
'track': {
'album': {
'name': item['track']['album']['name'],
},
'artists': [{
'name': item['track']['artists'][0]['name'],
}],
'name': item['track']['name'],
}
}
Then iterate and insert one by one:
for item in results['tracks']['items']:
track_dict = {
'track': {
'album': {
'name': item['track']['album']['name'],
},
'artists': [{
'name': item['track']['artists'][0]['name'],
}],
'name': item['track']['name'],
}
}
# Append the track dict structure to your results dict structure
result_dict['tracks']['items'].append(track_dict)
Having this as a result when printing result_dict:
{
'tracks': {
'items': [{
'track': {
'album': {
'name': 'ONLY'
},
'artists': [{
'name': 'ZHU'
}],
'name': 'ONLY'
}
}, {
'track': {
'album': {
'name': 'Bad 25th Anniversary'
},
'artists': [{
'name': 'Michael Jackson'
}],
'name': 'Bad - 2012 Remaster'
}
}, {
'track': {
'album': {
'name': 'Rodrigo y Gabriela'
},
'artists': [{
'name': 'Rodrigo y Gabriela'
}],
'name': 'Orion'
}
}, {
'track': {
'album': {
'name': '÷ (Deluxe)'
},
'artists': [{
'name': 'Ed Sheeran'
}],
'name': 'Shape of You'
}
}],
'limit': 100,
'next': None,
'offset': 0,
'previous': None,
'total': 4
},
'type': 'playlist',
'uri': '<playlist_uri>'
}
I much prefer the look of buttons to a numbered list, but the default handling of prompt in waterfall dialog is to automatically change from buttons (default) to a numbered list after a certain length of content.
I'm currently implementing the prompt like this:
return await step.prompt(FOCUS_AREA_PROMPT, {
prompt: 'Got it. Can you confirm the focus area this is for?',
choices: ChoiceFactory.toChoices(FOCUS_AREAS)
});
I've tried adding a style attribute within this prompt, and also tried adding to the addDialogs line this.dialogs.add(new ChoicePrompt(FOCUS_AREA_PROMPT)); but nothing I have tried has modified the behavior of the options.
I've reviewed the ListStyle enum in the MS docs, but any method I've tried to add these in with has not made any difference. Is there any way to force buttons regardless of content length?
You can set up a choice prompt in the following manner to achieve the buttons look you are seeking. For reference, you can read more about forChannel here.
Alter to match your needs.
Hope of help!
[edit]
Updated below to represent the two ways a Choice Prompt can be assembled and how the value is output (via imBack). When using toChoices, the associated button value is returned in activity.text and in stepContext.result.value (as type Object). When using forChannel, the associated button value is returned in activity.text and in stepContext.result (as type String).
As discussed in the comments, the button title length has a character limit however this is channel specific. When testing in Web Chat, the limit is 20 characters. Adjusting the FOCUS_AREAS "AI & Machine Learning" value (21 chars) to "AI/Machine Learning" (19 chars) results in the choices displaying as buttons and not a list.
Option 1: using toChoices
async choiceStep ( stepContext ) {
const stepResult = stepContext.context.activity.text;
const FOCUS_AREAS = [ 'Chatbots', 'RPA', 'Blockchain', 'AR/VR', 'AI/Machine Learning' ]
if ( stepResult ) {
return await stepContext.prompt( CHOICE_PROMPT, {
prompt: 'Got it. Can you confirm the focus area this is for?',
choices: ChoiceFactory.toChoices( FOCUS_AREAS )
} );
}
}
activity:
{ type: 'message',
id: 'A50eelAPrFIHKv9XeCRm24-o|0000021',
timestamp: 2019-09-25T20:34:30.562Z,
serviceUrl: 'https://directline.botframework.com/',
channelId: 'directline',
from: [Object],
conversation: [Object],
recipient: [Object],
textFormat: 'plain',
locale: 'en-US',
text: 'Chatbots',
channelData: [Object] },
info:
{ index: 1,
options: {},
reason: 'endCalled',
result:
{ value: 'Chatbots', index: 0, score: 1, synonym: 'Chatbots' },
values: { instanceId: 'c10ed437-77eb-4502-cd24-e89d4c5e45cf' },
onNext: [AsyncFunction: onNext] }
Option 2: using forChannel
async choiceStep ( stepContext ) {
const stepResult = stepContext.context.activity.text;
if ( stepResult ) {
const message = ChoiceFactory.forChannel(
stepContext.context, [
{ value: 'Chatbots', action: { type: 'imBack', title: 'Chatbots', value: 'Chatbots' } },
{ value: 'RPA', action: { type: 'imBack', title: 'RPA', value: 'RPA' } },
{ value: 'Blockchain', action: { type: 'imBack', title: 'Blockchain', value: 'Blockchain' } },
{ value: 'AR/VR', action: { type: 'imBack', title: 'AR/VR', value: 'AR/VR' } },
{ value: 'AI/Machine Learning', action: { type: 'imBack', title: '', value: 'AI/Machine Learning' }, text: 'AI/Machine Learning' },
], `Which do you choose?`
);
await stepContext.context.sendActivity( message );
}
return { status: DialogTurnStatus.waiting };
}
activity:
{ type: 'message',
id: 'Cw5xvHTv6RCDWf3kkyS3Ir-o|0000205',
timestamp: 2019-09-25T20:21:30.320Z,
serviceUrl: 'https://directline.botframework.com/',
channelId: 'directline',
from: [Object],
conversation: [Object],
recipient: [Object],
textFormat: 'plain',
locale: 'en-US',
text: 'Chatbots',
channelData: [Object] },
info:
{ index: 1,
options: {},
reason: 'continueCalled',
result: 'Chatbots',
values: { instanceId: '4becefed-88d2-773e-6184-91456609a26a' },
onNext: [AsyncFunction: onNext] }
I'm new using node-nlp but according to the examples I saw and the data I provided I should get a buy_food intent. But I'm not, it's returning hello intent. Any suggestions? If I remove the
manager.addDocument('en', 'hi', 'hello') line, it returns the right intent.
Any suggestions?
const { NlpManager } = require('node-nlp');
const manager = new NlpManager({ languages: 'en', ner: { threshold: 0.8 } });
manager.addNamedEntityText('drink', 'sprite', ['en'], ['sprite']);
manager.addNamedEntityText('size', 'large', ['en'], ['large', 'big']);
manager.addDocument('en', 'hello', 'hello');
manager.addDocument('en', 'hi', 'hello');
manager.addDocument('en', '%size% %drink%', 'buy_food');
manager.addDocument('en', 'i want %drink% please', 'buy_food');
manager.addDocument('en', 'i want %size% %drink% please', 'buy_food');
manager.addDocument('en', 'i want a %drink% please', 'buy_food');
manager.addDocument('en', 'i want a %size% %drink% please', 'buy_food');
manager.addDocument('en', 'i want to order a %size% %drink%', 'buy_food');
manager.addDocument('en', 'i want to order a %drink%', 'buy_food');
manager.addDocument('en', 'bye', 'bye');
manager.addDocument('en', 'bye bye', 'bye');
manager.addAnswer('en', 'hello', 'Welcome! How may I help you?');
manager.addAnswer('en', 'bye', 'Till next time');
manager.addAnswer('en', 'bye', 'see you soon!');
manager.addAnswer('en', 'buy_food', 'Got it, what else?');
manager.addAnswer('en', 'buy_food', 'Ok. Next item.');
var text = 'i want a large sprite';
(async () => {
await manager.train();
var result= await manager.process('en', text);
console.log(result);
})();
The result is
{ utterance: 'i want a large sprite',
locale: 'en',
languageGuessed: false,
localeIso2: 'en',
language: 'English',
domain: 'default',
classifications:
[ { label: 'hello', value: 0.5484776863751949 },
{ label: 'buy_food', value: 0.36024868417489125 },
{ label: 'bye', value: 0.09127362944991388 } ],
intent: 'hello',
score: 0.5484776863751949,
entities:
[ { start: 9,
end: 13,
len: 5,
levenshtein: 0,
accuracy: 1,
option: 'large',
sourceText: 'large',
entity: 'size',
utteranceText: 'large' },
{ start: 15,
end: 20,
len: 6,
levenshtein: 0,
accuracy: 1,
option: 'sprite',
sourceText: 'sprite',
entity: 'drink',
utteranceText: 'sprite' } ],
sentiment:
{ score: 0.29699999999999993,
comparative: 0.05939999999999999,
vote: 'positive',
numWords: 5,
numHits: 6,
type: 'senticon',
language: 'en' },
actions: [],
srcAnswer: 'Welcome! How may I help you?',
answer: 'Welcome! How may I help you?' }