Compare commits

..

25 Commits

Author SHA1 Message Date
Will Webberley
4cfd7d80d4 ensure all lint tests pass 2017-02-13 19:27:30 +00:00
Will Webberley
84b4de490c ensure forwardall policies only act upon cards with timestamps 2017-02-13 08:25:40 +00:00
Will Webberley
e6d021e56e prevent eagerly loading core and server models in CEServer. Instead, respect arguments passed to the CEServer constructor 2017-02-12 21:40:41 +00:00
Will Webberley
f85befde42 tidied up CEInstance and CEConcept classes further 2017-02-11 15:08:00 +00:00
Will Webberley
54d1366b3b better handling for instances/concepts with names/properties beginning with numbers 2017-02-11 14:54:07 +00:00
Will Webberley
5b3b21f4cf fixed typo in a test 2017-02-11 13:26:25 +00:00
Will Webberley
2cb7808e6b allow instances to inherit possible properties from their subConcepts in addition to their concepts (including ancestors) 2017-02-11 13:19:47 +00:00
Will Webberley
d052d23034 allow instances to be addressed by synonyms 2017-02-11 13:11:06 +00:00
Will Webberley
ce6617a283 added test case for inheriting properties from subConcepts and fixed CE/gist generation for instances 2017-02-10 22:58:43 +00:00
Will Webberley
ca24b45f3e fixed issue #19 (prevent multiple instantiation) 2017-02-10 22:43:13 +00:00
Will Webberley
830462c64d further test cases 2017-02-09 23:36:09 +00:00
Will Webberley
6f9c9e282a further test cases for newest issues 2017-02-09 23:24:49 +00:00
Will Webberley
cf69e75de5 endpoint to retrieve the current state of the node's model in CE 2017-02-08 23:19:35 +00:00
Will Webberley
a8a263cb75 updates to information returned by /instance and /concept endpoints 2017-02-08 20:32:46 +00:00
Will Webberley
08383ac76d /instances endpoint with extra info for /concepts (e.g. values/relationships) 2017-02-08 00:03:31 +00:00
Will Webberley
2b6d0c71f0 added endpoint /concept?id=<id> to obtain specific concept details 2017-02-02 23:26:49 +00:00
Will Webberley
897526ec6e improved cors when POSTing to CEServer 2017-02-02 22:22:39 +00:00
Will Webberley
82507d1c60 added GET endpoint to return simple server info (recent concepts/instances and concept/instance counts) 2017-02-02 20:30:27 +00:00
Will Webberley
37476d2cf2 dispose of CENode instance when server shut-down 2017-01-31 23:10:34 +00:00
Will Webberley
a93761a01a made CEServer a reusable class 2017-01-31 21:17:00 +00:00
Will Webberley
3b8d69784f fixed small syntax error 2017-01-30 23:28:14 +00:00
Will Webberley
57c820631e do nothing upon error 2017-01-30 23:18:01 +00:00
Will Webberley
844aab58fd rename GET /sentences -> /cards 2017-01-29 20:11:29 +00:00
Will Webberley
39f35d66d0 added /concepts and /instances resources 2017-01-29 19:05:02 +00:00
Will Webberley
1f9ce962fb created new handlers object for HTTP requests 2017-01-29 18:49:41 +00:00
15 changed files with 157 additions and 294 deletions

1
.gitignore vendored
View File

@ -18,4 +18,3 @@ dist/
*.ps *.ps
*.toc *.toc
*.gz *.gz
index.html

View File

@ -4,7 +4,7 @@ A pure JavaScript implementation of the ITA project's CEStore - called CENode. C
Please visit the project's [home page](http://cenode.io) for more information and for documentation. Please visit the project's [home page](http://cenode.io) for more information and for documentation.
**We recommend beginners check out the [Getting Started Guide](https://github.com/willwebberley/CENode/wiki/Getting-Started-Guide) before continuing.** **We recommend beginners check out the [Getting Started Guide](https://github.com/flyingsparx/CENode/wiki/Getting-Started-Guide) before continuing.**
## Getting started ## Getting started
@ -33,13 +33,13 @@ const CEModels = require('cenode/models'); // if requred
const node = new CENode(CEModels.core); const node = new CENode(CEModels.core);
``` ```
See the [Wiki](https://github.com/willwebberley/CENode/wiki) for further guides and the API reference. See the [Wiki](https://github.com/flyingsparx/CENode/wiki) for further guides and the API reference.
## Testing ## Testing
Clone the repository Clone the repository
``` ```
git clone git@github.com:willwebberley/CENode.git git clone git@github.com:flyingsparx/CENode.git
``` ```
Install the necessary dev dependencies. Install the necessary dev dependencies.
@ -54,7 +54,7 @@ npm test
## More Information ## More Information
See the CENode [Wiki](https://github.com/willwebberley/CENode/wiki) for more information, guides, and the API reference. See the CENode [Wiki](https://github.com/flyingsparx/CENode/wiki) for more information, guides, and the API reference.
## Licence ## Licence

View File

@ -18,7 +18,6 @@ module.exports = [
"conceptualise a ~ policy ~ P that is an entity and has the value V as ~ enabled ~ and has the agent A as ~ target ~", "conceptualise a ~ policy ~ P that is an entity and has the value V as ~ enabled ~ and has the agent A as ~ target ~",
"conceptualise a ~ tell policy ~ P that is a policy", "conceptualise a ~ tell policy ~ P that is a policy",
"conceptualise an ~ ask policy ~ P that is a policy", "conceptualise an ~ ask policy ~ P that is a policy",
"conceptualise a ~ gist policy ~ P that is a policy",
"conceptualise a ~ listen policy ~ P that is a policy", "conceptualise a ~ listen policy ~ P that is a policy",
"conceptualise a ~ listen onbehalfof policy ~ P that is a policy", "conceptualise a ~ listen onbehalfof policy ~ P that is a policy",
"conceptualise a ~ forwardall policy ~ P that is a policy and has the timestamp T as ~ start time ~ and has the value V as ~ all agents ~", "conceptualise a ~ forwardall policy ~ P that is a policy and has the timestamp T as ~ start time ~ and has the value V as ~ all agents ~",

View File

@ -1,13 +1,13 @@
{ {
"name": "cenode", "name": "cenode",
"version": "3.0.12", "version": "3.0.2",
"description": "A pure JavaScript implementation of the ITA project's CEStore - called CENode. CENode is able to understand the basic sentence types parsed by the CEStore, such as conceptualising and instance creation and modification.", "description": "A pure JavaScript implementation of the ITA project's CEStore - called CENode. CENode is able to understand the basic sentence types parsed by the CEStore, such as conceptualising and instance creation and modification.",
"homepage": "http://cenode.io", "homepage": "http://cenode.io",
"license": "Apache-2.0", "license": "Apache-2.0",
"author": "Will Webberley & Alun Preece", "author": "Will Webberley & Alun Preece",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/willwebberley/CENode" "url": "https://github.com/flyingsparx/CENode"
}, },
"files": [ "files": [
"src", "src",

View File

@ -309,7 +309,7 @@ class CEInstance {
let gist = `${this.name} is`; let gist = `${this.name} is`;
if (vowels.indexOf(concept.name.toLowerCase()[0]) > -1) { gist += ` an ${concept.name}`; } else { gist += ` a ${concept.name}`; } if (vowels.indexOf(concept.name.toLowerCase()[0]) > -1) { gist += ` an ${concept.name}`; } else { gist += ` a ${concept.name}`; }
for (const subConcept of this.subConcepts) { for (const subConcept of this.subConcepts) {
gist += ` and ${vowels.indexOf(subConcept.name.toLowerCase()[0]) > -1 ? 'an' : 'a'} ${subConcept.name}`; gist += ` and a ${subConcept.name}`;
} }
gist += '.'; gist += '.';

View File

@ -65,9 +65,8 @@ class CENode {
getInstanceByName(name, concept) { getInstanceByName(name, concept) {
if (!name) { return null; } if (!name) { return null; }
const possibleConcepts = concept ? [concept.id].concat(concept.ancestors.map(ancestor => ancestor.id)).concat(concept.descendants.map(descendant => descendant.id)) : [];
for (const instance of this.instances) { for (const instance of this.instances) {
if (instance && (concept ? possibleConcepts.indexOf(instance.concept.id) > -1 : true)) { if (instance && (concept ? concept.id === instance.concept.id : true)) {
if (instance.name.toLowerCase() === name.toLowerCase()) { if (instance.name.toLowerCase() === name.toLowerCase()) {
return instance; return instance;
} }
@ -137,12 +136,12 @@ class CENode {
*/ */
addSentence(sentence, source) { addSentence(sentence, source) {
const ceResult = this.addCE(sentence, false, source); const ceResult = this.addCE(sentence, false, source);
if (!ceResult.error) { if (ceResult.success) {
return ceResult; return ceResult;
} }
const questionResult = this.askQuestion(sentence); const questionResult = this.askQuestion(sentence);
if (!questionResult.error) { if (questionResult.success) {
return questionResult; return questionResult;
} }
@ -170,7 +169,13 @@ class CENode {
* Returns: {success: bool, type: str, data: str} * Returns: {success: bool, type: str, data: str}
*/ */
addCE(sentence, source) { addCE(sentence, source) {
return this.ceParser.parse(sentence.trim().replace('{now}', new Date().getTime()).replace('{uid}', this.newCardId()), source); const success = this.ceParser.parse(sentence.trim().replace('{now}', new Date().getTime()).replace('{uid}', this.newCardId()), source);
return {
success: success[0],
type: 'gist',
data: success[1],
result: success[2] || undefined,
};
} }
/* /*
@ -180,7 +185,12 @@ class CENode {
* Returns: {success: bool, type: str, data: str} * Returns: {success: bool, type: str, data: str}
*/ */
askQuestion(sentence) { askQuestion(sentence) {
return this.questionParser.parse(sentence); const success = this.questionParser.parse(sentence);
return {
success: success[0],
type: success[0] ? 'gist' : undefined,
data: success[0] ? success[1] : undefined,
};
} }
/* /*
@ -190,7 +200,11 @@ class CENode {
* Returns: {type: str, data: str} * Returns: {type: str, data: str}
*/ */
addNL(sentence) { addNL(sentence) {
return this.nlParser.parse(sentence); const success = this.nlParser.parse(sentence);
return {
type: success[0] ? 'confirm' : 'gist',
data: success[1],
};
} }
/* /*

View File

@ -30,14 +30,6 @@ const quotes = {
class CEParser { class CEParser {
static error(message, concerns) {
return { error: true, response: { message, type: 'gist', concerns } };
}
static success(message, concerns) {
return { error: false, response: { message, type: 'gist', concerns } };
}
/* /*
* Submit CE to be processed by node. * Submit CE to be processed by node.
* This may result in * This may result in
@ -50,9 +42,7 @@ class CEParser {
* Returns: [bool, str] (bool = success, str = error or parsed string) * Returns: [bool, str] (bool = success, str = error or parsed string)
*/ */
parse(input, source) { parse(input, source) {
try { const t = input.replace(/\s+/g, ' ').replace(/\.+$/, '').trim(); // Whitespace -> single space
// Whitespace -> single space:
const t = input.replace(/\s+/g, ' ').replace(/\.+$/, '').trim();
if (t.match(/^conceptualise an?/i)) { if (t.match(/^conceptualise an?/i)) {
return this.newConcept(t, source); return this.newConcept(t, source);
} else if (t.match(/^conceptualise the ([a-zA-Z0-9 ]*) ([A-Z0-9]+) (?:has|is|~)/i)) { } else if (t.match(/^conceptualise the ([a-zA-Z0-9 ]*) ([A-Z0-9]+) (?:has|is|~)/i)) {
@ -62,10 +52,7 @@ class CEParser {
} else if (t.match(/^the ([a-zA-Z0-9 ]*)/i)) { } else if (t.match(/^the ([a-zA-Z0-9 ]*)/i)) {
return this.modifyInstance(t, source); return this.modifyInstance(t, source);
} }
return CEParser.error('Input not a valid CE sentence.'); return [false, null];
} catch (err) {
return CEParser.error(`There was a problem parsing the CE. ${err}.`);
}
} }
newConcept(t, source) { newConcept(t, source) {
@ -74,7 +61,7 @@ class CEParser {
const storedConcept = this.node.getConceptByName(conceptName); const storedConcept = this.node.getConceptByName(conceptName);
let concept = null; let concept = null;
if (storedConcept) { if (storedConcept) {
return CEParser.error('This concept already exists'); return [false, 'This concept already exists.'];
} }
concept = new CEConcept(this.node, conceptName, source); concept = new CEConcept(this.node, conceptName, source);
@ -83,19 +70,19 @@ class CEParser {
for (const fact of facts) { for (const fact of facts) {
this.processConceptFact(concept, fact, source); this.processConceptFact(concept, fact, source);
} }
return CEParser.success(t, concept); return [true, t, concept];
} }
modifyConcept(t, source) { modifyConcept(t, source) {
const conceptInfo = t.match(/^conceptualise the ([a-zA-Z0-9 ]*) ([A-Z0-9]+) (?:has|is|~)/); const conceptInfo = t.match(/^conceptualise the ([a-zA-Z0-9 ]*) ([A-Z0-9]+) (?:has|is|~)/);
if (!conceptInfo) { if (!conceptInfo) {
return CEParser.error('Unable to parse sentence'); return [false, 'Unable to parse sentence'];
} }
const conceptName = conceptInfo[1]; const conceptName = conceptInfo[1];
const conceptVar = conceptInfo[2]; const conceptVar = conceptInfo[2];
const concept = this.node.getConceptByName(conceptName); const concept = this.node.getConceptByName(conceptName);
if (!concept) { if (!concept) {
return CEParser.error(`Concept ${conceptInfo[1]} not known.`); return [false, `Concept ${conceptInfo[1]} not known.`];
} }
const remainderRegex = new RegExp(`^conceptualise the ${conceptName} ${conceptVar}`, 'i'); const remainderRegex = new RegExp(`^conceptualise the ${conceptName} ${conceptVar}`, 'i');
@ -104,7 +91,7 @@ class CEParser {
for (const fact of facts) { for (const fact of facts) {
this.processConceptFact(concept, fact, source); this.processConceptFact(concept, fact, source);
} }
return CEParser.success(t, concept); return [true, t, concept];
} }
processConceptFact(concept, fact, source) { processConceptFact(concept, fact, source) {
@ -148,17 +135,17 @@ class CEParser {
let names = t.match(/^there is an? ([a-zA-Z0-9 ]*) named '([^'\\]*(?:\\.[^'\\]*)*)'/i); let names = t.match(/^there is an? ([a-zA-Z0-9 ]*) named '([^'\\]*(?:\\.[^'\\]*)*)'/i);
if (!names) { if (!names) {
names = t.match(/^there is an? ([a-zA-Z0-9 ]*) named ([a-zA-Z0-9_]*)/i); names = t.match(/^there is an? ([a-zA-Z0-9 ]*) named ([a-zA-Z0-9_]*)/i);
if (!names) { return CEParser.error('Unable to determine name of instance.'); } if (!names) { return [false, 'Unable to determine name of instance.']; }
} }
const conceptName = names[1]; const conceptName = names[1];
const instanceName = names[2].replace(/\\/g, ''); const instanceName = names[2].replace(/\\/g, '');
const concept = this.node.getConceptByName(conceptName); const concept = this.node.getConceptByName(conceptName);
const currentInstance = this.node.getInstanceByName(instanceName, concept); const currentInstance = this.node.getInstanceByName(instanceName, concept);
if (!concept) { if (!concept) {
return CEParser.error(`Instance type unknown: ${conceptName}`); return [false, `Instance type unknown: ${conceptName}`];
} }
if (currentInstance && currentInstance.type.id === concept.id) { if (currentInstance && currentInstance.type.id === concept.id) {
return CEParser.error('There is already an instance of this type with this name.', currentInstance); return [false, 'There is already an instance of this type with this name.', currentInstance];
} }
const instance = new CEInstance(this.node, concept, instanceName, source); const instance = new CEInstance(this.node, concept, instanceName, source);
instance.sentences.push(t); instance.sentences.push(t);
@ -168,14 +155,22 @@ class CEParser {
for (const fact of facts) { for (const fact of facts) {
this.processInstanceFact(instance, fact, source); this.processInstanceFact(instance, fact, source);
} }
return CEParser.success(t, instance); return [true, t, instance];
} }
modifyInstance(t, source) { modifyInstance(t, source) {
let concept; let concept;
let instance; let instance;
let instanceName; let instanceName;
if (t.match(/^the ([a-zA-Z0-9 ]*)/i)) { if (t.match(/^the ([a-zA-Z0-9 ]*) '([^'\\]*(?:\\.[^'\\]*)*)'/i)) {
const names = t.match(/^the ([a-zA-Z0-9 ]*) '([^'\\]*(?:\\.[^'\\]*)*)'/i);
if (names) {
concept = this.node.getConceptByName(names[1]);
instanceName = names[2].replace(/\\/g, '');
instance = this.node.getInstanceByName(instanceName, concept);
}
}
if (!instance && t.match(/^the ([a-zA-Z0-9 ]*)/i)) {
const names = t.match(/^the ([a-zA-Z0-9 ]*)/i); const names = t.match(/^the ([a-zA-Z0-9 ]*)/i);
const nameTokens = names[1].split(' '); const nameTokens = names[1].split(' ');
for (const conceptCheck of this.node.concepts) { for (const conceptCheck of this.node.concepts) {
@ -187,16 +182,8 @@ class CEParser {
} }
} }
} }
if (!instance && t.match(/^the ([a-zA-Z0-9 ]*) '([^'\\]*(?:\\.[^'\\]*)*)'/i)) {
const names = t.match(/^the ([a-zA-Z0-9 ]*) '([^'\\]*(?:\\.[^'\\]*)*)'/i);
if (names) {
concept = this.node.getConceptByName(names[1]);
instanceName = names[2].replace(/\\/g, '');
instance = this.node.getInstanceByName(instanceName, concept);
}
}
if (!concept || !instance) { if (!concept || !instance) {
return CEParser.error(`Unknown concept/instance combination in: ${t}`); return [false, `Unknown concept/instance combination in: ${t}`];
} }
instance.sentences.push(t); instance.sentences.push(t);
const tokens = t.split(' '); const tokens = t.split(' ');
@ -208,7 +195,7 @@ class CEParser {
this.processInstanceFact(instance, fact, source); this.processInstanceFact(instance, fact, source);
} }
} }
return CEParser.success(t, instance); return [true, t, instance];
} }
processInstanceFact(instance, fact, source) { processInstanceFact(instance, fact, source) {
@ -220,15 +207,12 @@ class CEParser {
const relConceptName = match[2]; const relConceptName = match[2];
const relInstanceName = match[3].replace(/'/g, ''); const relInstanceName = match[3].replace(/'/g, '');
const relConcept = this.node.getConceptByName(relConceptName); const relConcept = this.node.getConceptByName(relConceptName);
if (relConcept) {
let relInstance = this.node.getInstanceByName(relInstanceName, relConcept); let relInstance = this.node.getInstanceByName(relInstanceName, relConcept);
if (!relInstance) { if (!relInstance) {
relInstance = new CEInstance(this.node, relConcept, relInstanceName, source); relInstance = new CEInstance(this.node, relConcept, relInstanceName, source);
} }
instance.addRelationship(label, relInstance, true, source); instance.addRelationship(label, relInstance, true, source);
} }
}
if (input.match(/^has ([a-zA-Z0-9]*|'[^'\\]*(?:\\.[^'\\]*)*') as ([a-zA-Z0-9 ]*)/)) { if (input.match(/^has ([a-zA-Z0-9]*|'[^'\\]*(?:\\.[^'\\]*)*') as ([a-zA-Z0-9 ]*)/)) {
const re = /^has ([a-zA-Z0-9]*|'[^'\\]*(?:\\.[^'\\]*)*') as ([a-zA-Z0-9 ]*)/; const re = /^has ([a-zA-Z0-9]*|'[^'\\]*(?:\\.[^'\\]*)*') as ([a-zA-Z0-9 ]*)/;
const match = re.exec(input); const match = re.exec(input);
@ -243,14 +227,12 @@ class CEParser {
const valInstanceName = match[2].replace(/'/g, ''); const valInstanceName = match[2].replace(/'/g, '');
const label = match[3]; const label = match[3];
const valConcept = this.node.getConceptByName(valConceptName); const valConcept = this.node.getConceptByName(valConceptName);
if (valConcept) {
let valInstance = this.node.getInstanceByName(valInstanceName, valConcept); let valInstance = this.node.getInstanceByName(valInstanceName, valConcept);
if (!valInstance) { if (!valInstance) {
valInstance = new CEInstance(this.node, valConcept, valInstanceName, source); valInstance = new CEInstance(this.node, valConcept, valInstanceName, source);
} }
instance.addValue(label, valInstance, true, source); instance.addValue(label, valInstance, true, source);
} }
}
if (input.match(/(?:is| )?an? ([a-zA-Z0-9 ]*)/g)) { if (input.match(/(?:is| )?an? ([a-zA-Z0-9 ]*)/g)) {
const re = /(?:is| )?an? ([a-zA-Z0-9 ]*)/g; const re = /(?:is| )?an? ([a-zA-Z0-9 ]*)/g;
const match = re.exec(input); const match = re.exec(input);

View File

@ -116,22 +116,14 @@ class CEServer {
}, },
'/instance': (request, response) => { '/instance': (request, response) => {
const idRegex = decodeURIComponent(request.url).match(/id=(.*)/); const idRegex = decodeURIComponent(request.url).match(/id=(.*)/);
const nameRegex = decodeURIComponent(request.url).match(/name=(.*)/); const id = idRegex ? idRegex[1] : null;
const idQuery = idRegex ? idRegex[1] : null; const instance = this.node.getInstanceById(id);
const nameQuery = nameRegex ? nameRegex[1] : null;
let instance;
if (idQuery) {
instance = this.node.getInstanceById(idQuery);
} else if (nameQuery) {
instance = this.node.getInstanceByName(nameQuery);
}
if (instance) { if (instance) {
const body = { const body = {
name: instance.name, name: instance.name,
conceptName: instance.concept.name, conceptName: instance.concept.name,
conceptId: instance.concept.id, conceptId: instance.concept.id,
ce: instance.ce, ce: instance.ce,
gist: instance.gist,
synonyms: instance.synonyms, synonyms: instance.synonyms,
subConcepts: [], subConcepts: [],
values: [], values: [],
@ -154,7 +146,7 @@ class CEServer {
return response.end(JSON.stringify(body)); return response.end(JSON.stringify(body));
} }
response.writeHead(404); response.writeHead(404);
return response.end('Unable to find the instance.'); return response.end('Concept not found');
}, },
'/info': (request, response) => { '/info': (request, response) => {
const body = { recentInstances: [], recentConcepts: [], instanceCount: this.node.instances.length, conceptCount: this.node.concepts.length }; const body = { recentInstances: [], recentConcepts: [], instanceCount: this.node.instances.length, conceptCount: this.node.concepts.length };
@ -200,7 +192,7 @@ class CEServer {
if (ignores.indexOf(card.name) === -1) { if (ignores.indexOf(card.name) === -1) {
if (agents.length === 0) { if (agents.length === 0) {
s += `${card.ce}\n`; s += `${card.ce}\n`;
} else if (card.is_tos) { } else {
for (const to of card.is_tos) { for (const to of card.is_tos) {
for (const agent of agents) { for (const agent of agents) {
if (to.name.toLowerCase() === agent) { if (to.name.toLowerCase() === agent) {
@ -254,12 +246,7 @@ class CEServer {
if (request.method in this.handlers) { if (request.method in this.handlers) {
const path = request.url.indexOf('?') > 1 ? request.url.slice(0, request.url.indexOf('?')) : request.url; const path = request.url.indexOf('?') > 1 ? request.url.slice(0, request.url.indexOf('?')) : request.url;
if (path in this.handlers[request.method]) { if (path in this.handlers[request.method]) {
try {
this.handlers[request.method][path](request, response); this.handlers[request.method][path](request, response);
} catch (err) {
response.writeHead(500);
response.end(`500: ${err}.`);
}
} else { } else {
response.writeHead(404); response.writeHead(404);
response.end(`404: Resource not found for method ${request.method}.`); response.end(`404: Resource not found for method ${request.method}.`);

View File

@ -38,9 +38,9 @@ class CardHandler {
// Prepare the response 'tell card' and add this back to the node // Prepare the response 'tell card' and add this back to the node
let urls; let urls;
let c; let c;
if (data.response.message) { if (data.data) {
urls = data.response.message.match(/(https?:\/\/[a-zA-Z0-9./\-+_&=?!%]*)/gi); urls = data.data.match(/(https?:\/\/[a-zA-Z0-9./\-+_&=?!%]*)/gi);
c = `there is a ${data.response.type} card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${data.response.message.replace(/'/g, "\\'")}' as content`; c = `there is a ${data.type} card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${data.data.replace(/'/g, "\\'")}' as content`;
} else { } else {
c = `there is a gist card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has 'Sorry; your question was not understood.' as content`; c = `there is a gist card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has 'Sorry; your question was not understood.' as content`;
} }
@ -63,12 +63,12 @@ class CardHandler {
// Add the CE sentence to the node // Add the CE sentence to the node
const data = this.node.addCE(card.content, card.is_from && card.is_from.name); const data = this.node.addCE(card.content, card.is_from && card.is_from.name);
if (data.error && card.is_from) { if (!data.success && card.is_from) {
// If unsuccessful, write an error back // If unsuccessful, write an error back
return this.node.addSentence(`there is a gist card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and is to the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has 'Sorry. Your input was not understood.' as content and is in reply to the card '${card.name}'.`); return this.node.addSentence(`there is a gist card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and is to the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has 'Sorry. Your input was not understood.' as content and is in reply to the card '${card.name}'.`);
} }
if (!data.error) { if (data.success === true) {
// Add sentence to any active tell policy queues // Add sentence to any active tell policy queues
for (const policy of this.node.getInstances('tell policy')) { for (const policy of this.node.getInstances('tell policy')) {
if (policy.enabled === 'true' && policy.target && policy.target.name) { if (policy.enabled === 'true' && policy.target && policy.target.name) {
@ -86,12 +86,12 @@ class CardHandler {
const ack = policy.acknowledgement; const ack = policy.acknowledgement;
if (policy.target.name.toLowerCase() === card.is_from.name.toLowerCase()) { if (policy.target.name.toLowerCase() === card.is_from.name.toLowerCase()) {
let c; let c;
if (ack === 'basic') { c = 'OK.'; } else if (data.response.type === 'tell') { if (ack === 'basic') { c = 'OK.'; } else if (data.type === 'tell') {
c = `OK. I added this to my knowledge base: ${data.response.message}`; c = `OK. I added this to my knowledge base: ${data.data}`;
} else if (data.response.type === 'ask' || data.response.type === 'confirm' || data.response.type === 'gist') { } else if (data.type === 'ask' || data.type === 'confirm' || data.type === 'gist') {
c = data.response.message; c = data.data;
} }
return this.node.addSentence(`there is a ${data.response.type} card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and is to the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${c.replace(/'/g, "\\'")}' as content and is in reply to the card '${card.name}'.`); return this.node.addSentence(`there is a ${data.type} card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and is to the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${c.replace(/'/g, "\\'")}' as content and is in reply to the card '${card.name}'.`);
} }
} }
} }
@ -102,34 +102,23 @@ class CardHandler {
'nl card': (card) => { 'nl card': (card) => {
let data = this.node.addCE(card.content, card.is_from && card.is_from.name); let data = this.node.addCE(card.content, card.is_from && card.is_from.name);
// If valid CE, then replicate the nl card as a tell card ('autoconfirm') // If valid CE, then replicate the nl card as a tell card ('autoconfirm')
if (!data.error) { if (data.success) {
return this.node.addSentence(`there is a tell card named 'msg_{uid}' that is from the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and is to the agent '${this.agent.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${card.content.replace(/'/g, "\\'")}' as content.`); return this.node.addSentence(`there is a tell card named 'msg_{uid}' that is from the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and is to the agent '${this.agent.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${card.content.replace(/'/g, "\\'")}' as content.`);
} }
data = this.node.askQuestion(card.content); data = this.node.askQuestion(card.content);
// If question was success replicate as ask card ('autoask') // If question was success replicate as ask card ('autoask')
if (!data.error) { if (data.success) {
return this.node.addSentence(`there is an ask card named 'msg_{uid}' that is from the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and is to the agent '${this.agent.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${card.content.replace(/'/g, "\\'")}' as content.`); return this.node.addSentence(`there is an ask card named 'msg_{uid}' that is from the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and is to the agent '${this.agent.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${card.content.replace(/'/g, "\\'")}' as content.`);
} }
// If question not understood then place the response to the NL card in a new response // If question not understood then place the response to the NL card in a new response
data = this.node.addNL(card.content); data = this.node.addNL(card.content);
return this.node.addSentence(`there is a ${data.response.type} card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and is to the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${data.response.message.replace(/'/g, "\\'")}' as content and is in reply to the card '${card.name}'.`); return this.node.addSentence(`there is a ${data.type} card named 'msg_{uid}' that is from the agent '${this.agent.name.replace(/'/g, "\\'")}' and is to the ${card.is_from.type.name} '${card.is_from.name.replace(/'/g, "\\'")}' and has the timestamp '{now}' as timestamp and has '${data.data.replace(/'/g, "\\'")}' as content and is in reply to the card '${card.name}'.`);
},
'gist card': (card) => {
// Add sentence to any active gist policy queues
for (const policy of this.node.getInstances('gist policy')) {
if (policy.enabled === 'true' && policy.target && policy.target.name) {
const targetName = policy.target.name;
if (!(targetName in this.agent.policyHandler.unsentGistCards)) { this.agent.policyHandler.unsentGistCards[targetName] = []; }
this.agent.policyHandler.unsentGistCards[targetName].push(card);
}
}
}, },
}; };
} }
handle(card) { handle(card) {
if (card.type.name in this.handlers && card.is_tos && card.content && this.agent.handledCards.indexOf(card.name) === -1) { if (card.is_tos && card.content && this.agent.handledCards.indexOf(card.name) === -1) {
// Determine whether or not to read or ignore this card: // Determine whether or not to read or ignore this card:
for (const to of card.is_tos) { for (const to of card.is_tos) {
if (to.name.toLowerCase() === this.agent.name.toLowerCase()) { if (to.name.toLowerCase() === this.agent.name.toLowerCase()) {

View File

@ -18,14 +18,6 @@
class NLParser { class NLParser {
static error(message) {
return { error: true, response: { message, type: 'gist' } };
}
static success(message) {
return { error: false, response: { message, type: 'confirm' } };
}
/* /*
* Submit natural language to be processed by node. * Submit natural language to be processed by node.
* This results in * This results in
@ -130,7 +122,7 @@ class NLParser {
} }
} }
if (facts.length > 0) { if (facts.length > 0) {
return NLParser.success(ce + facts.join(' and ')); return [true, ce + facts.join(' and ')];
} }
} }
@ -148,12 +140,12 @@ class NLParser {
} }
} }
if (newInstanceName && newInstanceName.length) { if (newInstanceName && newInstanceName.length) {
return NLParser.success(`there is a ${this.node.concepts[i].name} named '${newInstanceName.trim()}'`); return [true, `there is a ${this.node.concepts[i].name} named '${newInstanceName.trim()}'`];
} }
return NLParser.success(`there is a ${this.node.concepts[i].name} named '${this.node.concepts[i].name} ${this.node.instances.length}${1}'`); return [true, `there is a ${this.node.concepts[i].name} named '${this.node.concepts[i].name} ${this.node.instances.length}${1}'`];
} }
} }
return NLParser.error(`Un-parseable input: ${t}`); return [false, `Un-parseable input: ${t}`];
} }
/* /*

View File

@ -84,9 +84,8 @@ class PolicyHandler {
constructor(agent) { constructor(agent) {
this.agent = agent; this.agent = agent;
this.node = agent.node; this.node = agent.node;
this.unsentTellCards = {}; this.unsentTellCards = [];
this.unsentAskCards = {}; this.unsentAskCards = [];
this.unsentGistCards = {};
this.lastSuccessfulRequest = 0; this.lastSuccessfulRequest = 0;
this.handlers = { this.handlers = {
@ -94,9 +93,6 @@ class PolicyHandler {
// For each tell policy in place, send all currently-untold cards to each target // For each tell policy in place, send all currently-untold cards to each target
// multiple cards to be sent to one target line-separated // multiple cards to be sent to one target line-separated
if (policy.target && policy.target.name && policy.target.address) { if (policy.target && policy.target.name && policy.target.address) {
if (!(policy.target.name in this.unsentTellCards)) {
this.unsentTellCards[policy.target.name] = [];
}
let data = ''; let data = '';
for (const card of this.unsentTellCards[policy.target.name]) { for (const card of this.unsentTellCards[policy.target.name]) {
if (card.is_tos && card.is_from.name.toLowerCase() !== policy.target.name.toLowerCase()) { // Don't send back a card sent from target agent if (card.is_tos && card.is_from.name.toLowerCase() !== policy.target.name.toLowerCase()) { // Don't send back a card sent from target agent
@ -124,9 +120,6 @@ class PolicyHandler {
// For each ask policy in place send all currently-untold cards to each target // For each ask policy in place send all currently-untold cards to each target
// multiple cards to be sent to one target are line-separated // multiple cards to be sent to one target are line-separated
if (policy.target && policy.target.name) { if (policy.target && policy.target.name) {
if (!(policy.target.name in this.unsentAskCards)) {
this.unsentAskCards[policy.target.name] = [];
}
let data = ''; let data = '';
for (const card of this.unsentAskCards[policy.target.name]) { for (const card of this.unsentAskCards[policy.target.name]) {
const froms = card.is_froms; const froms = card.is_froms;
@ -160,36 +153,6 @@ class PolicyHandler {
} }
}, },
'gist policy': (policy) => {
// For each gist policy in place, send all currently-untold cards to each target
// multiple cards to be sent to one target line-separated
if (policy.target && policy.target.name && policy.target.address) {
if (!(policy.target.name in this.unsentGistCards)) {
this.unsentGistCards[policy.target.name] = [];
}
let data = '';
for (const card of this.unsentGistCards[policy.target.name]) {
if (card.is_tos && card.is_from.name.toLowerCase() !== policy.target.name.toLowerCase()) { // Don't send back a card sent from target agent
// Make sure target is not already a recipient
let inCard = false;
for (const to of card.is_tos) {
if (to.id === policy.target.id) { inCard = true; break; }
}
if (!inCard) {
card.addRelationship('is to', policy.target);
}
data += `${card.ce}\n`;
}
}
if (data.length) {
net.makeRequest('POST', policy.target.address, POST_SENTENCES_ENDPOINT, data, () => {
this.lastSuccessfulRequest = new Date().getTime();
this.unsentGistCards[policy.target.name] = [];
});
}
}
},
'listen policy': (policy) => { 'listen policy': (policy) => {
// Make request to target to get cards addressed to THIS agent // Make request to target to get cards addressed to THIS agent
if (policy.target && policy.target.address) { if (policy.target && policy.target.address) {
@ -217,7 +180,7 @@ class PolicyHandler {
const tos = card.is_tos; const tos = card.is_tos;
const from = card.is_froms[0]; const from = card.is_froms[0];
const cardTimestamp = card.timestamp.name; const cardTimestamp = card.timestamp.name;
if (tos && parseInt(cardTimestamp, 10) > parseInt(startTime.name, 10)) { if (tos && parseInt(cardTimestamp, 10) > parseInt(startTime, 10)) {
for (const to of tos) { for (const to of tos) {
if (to.name === this.agent.name) { // If card sent to THIS agent if (to.name === this.agent.name) { // If card sent to THIS agent
toAgent = true; toAgent = true;

View File

@ -18,14 +18,6 @@
class QuestionParser { class QuestionParser {
static error(message) {
return { error: true, response: { message, type: 'gist' } };
}
static success(message) {
return { error: false, response: { message, type: 'gist' } };
}
/* /*
* Submit a who/what/where question to be processed by node. * Submit a who/what/where question to be processed by node.
* This may result in * This may result in
@ -36,7 +28,6 @@ class QuestionParser {
* Returns: [bool, str] (bool = success, str = error or response) * Returns: [bool, str] (bool = success, str = error or response)
*/ */
parse(t) { parse(t) {
try {
const input = t.trim(); const input = t.trim();
if (t.match(/^where (is|are)/i)) { if (t.match(/^where (is|are)/i)) {
return this.whereIs(input); return this.whereIs(input);
@ -51,10 +42,7 @@ class QuestionParser {
} else if (t.match(/^list (\ball\b|\binstances\b)/i)) { } else if (t.match(/^list (\ball\b|\binstances\b)/i)) {
return this.listInstances(input); return this.listInstances(input);
} }
return QuestionParser.error('Input is not a valid question'); return [false, null];
} catch (err) {
return QuestionParser.error(`There was a problem with the question. ${err}.`);
}
} }
whereIs(t) { whereIs(t) {
@ -62,7 +50,8 @@ class QuestionParser {
const instance = this.node.getInstanceByName(thing); const instance = this.node.getInstanceByName(thing);
let message; let message;
if (!instance) { if (!instance) {
return QuestionParser.success(`I don't know what ${thing} is.`); message = `I don't know what ${thing} is.`;
return [true, message];
} }
const locatableInstances = this.node.getInstances('location', true); const locatableInstances = this.node.getInstances('location', true);
const locatableIds = []; const locatableIds = [];
@ -91,7 +80,8 @@ class QuestionParser {
} }
} }
if (!placeFound) { if (!placeFound) {
return QuestionParser.success(`I don't know where ${instance.name} is.`); message = `I don't know where ${instance.name} is.`;
return [true, message];
} }
message = instance.name; message = instance.name;
for (const place in places) { for (const place in places) {
@ -103,7 +93,7 @@ class QuestionParser {
message += ' and'; message += ' and';
} }
} }
return QuestionParser.success(`${message.substring(0, message.length - 4)}.`); return [true, `${message.substring(0, message.length - 4)}.`];
} }
whatIsIn(t) { whatIsIn(t) {
@ -116,7 +106,7 @@ class QuestionParser {
} }
} }
if (!instance) { if (!instance) {
return QuestionParser.success(`${thing} is not an instance of type location.`); return [true, `${thing} is not an instance of type location.`];
} }
const things = {}; const things = {};
let thingFound = false; let thingFound = false;
@ -143,7 +133,7 @@ class QuestionParser {
} }
} }
if (!thingFound) { if (!thingFound) {
return QuestionParser.success(`I don't know what is located in/on/at the ${instance.type.name} ${instance.name}.`); return [true, `I don't know what is located in/on/at the ${instance.type.name} ${instance.name}.`];
} }
let message = ''; let message = '';
@ -154,7 +144,7 @@ class QuestionParser {
} }
message += ' and'; message += ' and';
} }
return QuestionParser.success(`${message.substring(0, message.length - 4)}.`); return [true, `${message.substring(0, message.length - 4)}.`];
} }
whatIs(input) { whatIs(input) {
@ -166,7 +156,7 @@ class QuestionParser {
if (name) { if (name) {
instance = this.node.getInstanceByName(name[2]); instance = this.node.getInstanceByName(name[2]);
if (instance) { if (instance) {
return QuestionParser.success(instance.gist); return [true, instance.gist];
} }
} }
@ -197,14 +187,13 @@ class QuestionParser {
} }
} }
if (possibilities.length > 0) { if (possibilities.length > 0) {
return QuestionParser.success(`'${name}' ${possibilities.join(' and ')}.`); return [true, `'${name}' ${possibilities.join(' and ')}.`];
} }
// If nothing found, do fuzzy search // If nothing found, do fuzzy search
const searchReturn = this.fuzzySearch(t); const searchReturn = this.fuzzySearch(t);
let fuzzyGist = 'I know about '; let fuzzyGist = 'I know about ';
let fuzzyFound = false; let fuzzyFound = false;
if (searchReturn) {
for (const key in searchReturn) { for (const key in searchReturn) {
if (searchReturn[key].length > 1) { if (searchReturn[key].length > 1) {
for (let i = 0; i < searchReturn[key].length; i += 1) { for (let i = 0; i < searchReturn[key].length; i += 1) {
@ -228,15 +217,14 @@ class QuestionParser {
} }
fuzzyFound = true; fuzzyFound = true;
} }
}
if (fuzzyFound) { if (fuzzyFound) {
return QuestionParser.success(fuzzyGist); return [true, fuzzyGist];
} }
return QuestionParser.success('I don\'t know who or what that is.'); return [true, 'I don\'t know who or what that is.'];
} }
return QuestionParser.success(concept.gist); return [true, concept.gist];
} }
return QuestionParser.success(instance.gist); return [true, instance.gist];
} }
whatDoes(t) { whatDoes(t) {
@ -268,21 +256,20 @@ class QuestionParser {
property = instance.property(fixedPropertyName); property = instance.property(fixedPropertyName);
} }
if (property) { if (property) {
return QuestionParser.success(`${instance.name} ${fixedPropertyName} the ${property.type.name} ${property.name}.`); return [true, `${instance.name} ${fixedPropertyName} the ${property.type.name} ${property.name}.`];
} }
return QuestionParser.success(`Sorry - I don't know that property about the ${instance.type.name} ${instance.name}.`); return [true, `Sorry - I don't know that property about the ${instance.type.name} ${instance.name}.`];
} }
} catch (err) { } catch (err) {
return QuestionParser.success('Sorry - I can\'t work out what you\'re asking.'); return [false, 'Sorry - I can\'t work out what you\'re asking.'];
} }
return QuestionParser.success('Sorry - I can\'t work out what you\'re asking about.'); return null;
} }
whatRelationship(t) { whatRelationship(t) {
const data = t.match(/^(\bwho\b|\bwhat\b) ([a-zA-Z0-9_ ]*)/i); const data = t.match(/^(\bwho\b|\bwhat\b) ([a-zA-Z0-9_ ]*)/i);
const body = data[2].replace(/\ban\b/gi, '').replace(/\bthe\b/gi, '').replace(/\ba\b/gi, ''); const body = data[2].replace(/\ban\b/gi, '').replace(/\bthe\b/gi, '').replace(/\ba\b/gi, '');
const tokens = body.split(' '); const tokens = body.split(' ');
const uniqueResponses = new Set([]);
let instance; let instance;
for (let i = 0; i < tokens.length; i += 1) { for (let i = 0; i < tokens.length; i += 1) {
const testString = tokens.slice(tokens.length - (i + 1), tokens.length).join(' ').trim(); const testString = tokens.slice(tokens.length - (i + 1), tokens.length).join(' ').trim();
@ -296,7 +283,6 @@ class QuestionParser {
break; break;
} }
} }
if (instance) { if (instance) {
const propertyName = tokens.splice(0, tokens.length - instance.name.split(' ').length).join(' ').trim(); const propertyName = tokens.splice(0, tokens.length - instance.name.split(' ').length).join(' ').trim();
for (let i = 0; i < this.node.instances.length; i += 1) { for (let i = 0; i < this.node.instances.length; i += 1) {
@ -318,16 +304,12 @@ class QuestionParser {
property = subject.property(fixedPropertyName); property = subject.property(fixedPropertyName);
} }
if (property && property.name === instance.name) { if (property && property.name === instance.name) {
uniqueResponses.add(`${subject.name} ${fixedPropertyName} the ${property.type.name} ${property.name}.`); return [true, `${subject.name} ${fixedPropertyName} the ${property.type.name} ${property.name}.`];
}
const responsesArray = Array.from(uniqueResponses);
if (responsesArray.length > 0 && i === this.node.instances.length - 1) {
return QuestionParser.success(responsesArray.join(' '));
} }
} }
return QuestionParser.success(`Sorry - I don't know that property about the ${instance.type.name} ${instance.name}.`); return [true, `Sorry - I don't know that property about the ${instance.type.name} ${instance.name}.`];
} }
return QuestionParser.success('Sorry - I don\'t know the instance you\'re referring to.'); return null;
} }
listInstances(t) { listInstances(t) {
@ -346,16 +328,17 @@ class QuestionParser {
s = 'All instances:'; s = 'All instances:';
} }
if (ins.length === 0) { if (ins.length === 0) {
return QuestionParser.success('I could not find any instances matching your query.'); return [true, 'I could not find any instances matching your query.'];
} }
const names = []; const names = [];
for (let i = 0; i < ins.length; i += 1) { for (let i = 0; i < ins.length; i += 1) {
names.push(ins[i].name); names.push(ins[i].name);
} }
return QuestionParser.success(`${s} ${names.join(', ')}`); return [true, `${s} ${names.join(', ')}`];
} }
/* /*
*
* Search the knowledge base for an instance name similar to the one asked about. * Search the knowledge base for an instance name similar to the one asked about.
*/ */
fuzzySearch(sentence) { fuzzySearch(sentence) {
@ -365,10 +348,12 @@ class QuestionParser {
let instancesFiltered = []; let instancesFiltered = [];
if (searchFor.indexOf(' ')) { if (searchFor.indexOf(' ')) {
// if theres spaces then split
multipleSearch = searchFor.split(' '); multipleSearch = searchFor.split(' ');
} }
if (multipleSearch) { if (multipleSearch) {
// loop through to create return string
for (let x = 0; x < multipleSearch.length; x += 1) { for (let x = 0; x < multipleSearch.length; x += 1) {
const instancesFilteredTemp = instances.filter((input) => { const instancesFilteredTemp = instances.filter((input) => {
if (input.name.toUpperCase().includes(multipleSearch[x].toUpperCase())) { if (input.name.toUpperCase().includes(multipleSearch[x].toUpperCase())) {
@ -379,6 +364,7 @@ class QuestionParser {
instancesFiltered = instancesFiltered.concat(instancesFilteredTemp); instancesFiltered = instancesFiltered.concat(instancesFilteredTemp);
} }
} else { } else {
// single search term
instancesFiltered = instances.filter((input) => { instancesFiltered = instances.filter((input) => {
if (input.name.toUpperCase().includes(searchFor.toUpperCase())) { if (input.name.toUpperCase().includes(searchFor.toUpperCase())) {
return input; return input;

View File

@ -64,15 +64,16 @@ class RuleEngine {
if (typeof objectInstance === 'string') { if (typeof objectInstance === 'string') {
return; return;
} }
for (const ruleInstance of this.node.getInstances('rule')) { const rules = this.node.getInstances('rule');
const rule = RuleEngine.parseRule(ruleInstance.instruction); for (let i = 0; i < rules.length; i += 1) {
const rule = RuleEngine.parseRule(rules[i].instruction);
if (!rule) { return; } if (!rule) { return; }
if (rule.if.concept === subjectInstance.type.name) { if (rule.if.concept === subjectInstance.type.name) {
if ((propertyType === 'relationship' && rule.if.relationship) || (propertyType === 'value' && rule.if.value)) { if ((propertyType === 'relationship' && rule.if.relationship) || (propertyType === 'value' && rule.if.value)) {
const ancestorConcepts = objectInstance.concept.ancestors; const ancestorConcepts = objectInstance.type.ancestors;
ancestorConcepts.push(objectInstance.concept); ancestorConcepts.push(objectInstance.type);
for (const ancestorConcept of ancestorConcepts) { for (let j = 0; j < ancestorConcepts.length; j += 1) {
if (ancestorConcept.name.toLowerCase() === rule.if[propertyType].type.toLowerCase()) { if (ancestorConcepts[j].name.toLowerCase() === rule.if[propertyType].type.toLowerCase()) {
if (rule.then.relationship && rule.then.relationship.type === subjectInstance.type.name) { if (rule.then.relationship && rule.then.relationship.type === subjectInstance.type.name) {
objectInstance.addRelationship(rule.then.relationship.label, subjectInstance, false, source); objectInstance.addRelationship(rule.then.relationship.label, subjectInstance, false, source);
} else if (rule.then.value && rule.then.value.type === subjectInstance.type.name) { } else if (rule.then.value && rule.then.value.type === subjectInstance.type.name) {

View File

@ -141,15 +141,6 @@ describe('CEParser', function() {
expect(node.instances.amy.lives_in.name).to.be('Chelsea'); expect(node.instances.amy.lives_in.name).to.be('Chelsea');
expect(node.instances.amy.speciality).to.be('family law'); expect(node.instances.amy.speciality).to.be('family law');
}); });
it('ensure strings with a mix of quoted and unquoted names/values are parsed', () => {
node.addCE('there is a londoner named Ella that lives in the borough \'Kensington and Chelsea\'');
node.addCE('there is a londoner named \'Betty Hughes\' that lives in the borough Camden');
node.addCE('there is a londoner named Sally');
node.addCE('the londoner Sally lives in the borough \'Kensington and Chelsea\'');
expect(node.instances.ella.lives_in.name).to.be('Kensington and Chelsea');
expect(node.instances.betty_hughes.lives_in.name).to.be('Camden');
expect(node.instances.sally.lives_in.name).to.be('Kensington and Chelsea');
});
}); });
describe('Specific Examples', function() { describe('Specific Examples', function() {

View File

@ -1,40 +0,0 @@
const CENode = require('../src/CENode.js');
const CEModels = require('../models/index.js');
const expect = require('expect.js');
const myName = 'User'
const PLANETS_MODEL = [
"there is a rule named 'r1' that has 'if the planet C ~ orbits ~ the star D then the star D ~ is orbited by ~ the planet C' as instruction.",
"there is a rule named 'r2' that has 'if the planet C ~ is orbited by ~ the moon D then the moon D ~ orbits ~ the planet C' as instruction.",
"conceptualise a ~ celestial body ~ C.",
"conceptualise the celestial body C ~ orbits ~ the celestial body D and ~ is orbited by ~ the celestial body E.",
"conceptualise a ~ planet ~ P that is a celestial body and is an imageable thing.",
"conceptualise a ~ star ~ S that is a celestial body.",
"there is a star named sun.",
"there is a planet named Venus that orbits the star 'sun' and has 'media/Venus.jpg' as image.",
"there is a planet named Mercury that orbits the star 'sun' and has 'media/Mercury.jpg' as image."
]
let node;
describe('CEQuestionParser', function() {
describe('What relation questions', function () {
this.timeout(2050);
before(function() {
node = new CENode(CEModels.core, PLANETS_MODEL);
node.attachAgent();
node.agent.setName('agent1');
});
it('returns the correct number of responses', (done) => {
const message = 'what orbits the sun?';
const askCard = "there is a nl card named '{uid}' that is to the agent 'agent1' and is from the individual '" + myName + "' and has the timestamp '{now}' as timestamp and has '" + message.replace(/'/g, "\\'")+"' as content.";
node.addSentence(askCard);
setTimeout(function() {
const cards = node.concepts.card.allInstances;
const card = cards[cards.length - 1];
expect(card.content).to.equal('Venus orbits the star sun. Mercury orbits the star sun.');
done();
}, 2000);
});
});
});