Compare commits
96 Commits
master
...
v3-updates
Author | SHA1 | Date | |
---|---|---|---|
|
372c9c4941 | ||
|
93513a42b6 | ||
|
6fcc8d4508 | ||
|
6f335609f6 | ||
|
24cbe22ac7 | ||
|
cad66fffbe | ||
|
be38274eb7 | ||
|
aaad163ed9 | ||
|
6916a2bfa0 | ||
|
6b340a8b7b | ||
|
02cb4e1400 | ||
|
4692478719 | ||
|
8976b9544c | ||
|
1c644219fd | ||
|
43d61a909c | ||
|
c48bcd403f | ||
|
195b082261 | ||
|
0af13a951b | ||
|
600c08d223 | ||
|
b10c6be7c4 | ||
|
0205f12ab8 | ||
|
5b3784947e | ||
|
8c8d598c00 | ||
|
b84d5ce709 | ||
|
4ac0c2b696 | ||
|
aabd510b61 | ||
|
5ed7886e93 | ||
|
12ac2e6dd5 | ||
|
f090b875aa | ||
|
7e9cee3a4d | ||
|
0417d39d88 | ||
|
36ec8c80b7 | ||
|
9ffb996771 | ||
|
86611ba13d | ||
|
1690ebbef3 | ||
|
c0d8d4c938 | ||
|
372023e4d8 | ||
|
a1310118dc | ||
|
f1237be883 | ||
|
6d305de736 | ||
|
28d36630ea | ||
|
0536ea693b | ||
|
f8aa6a260c | ||
|
e81f7a2d69 | ||
|
190d3412a3 | ||
|
81c727fe3f | ||
|
1734d023ab | ||
|
33ed6ea06f | ||
|
0990bb013e | ||
|
074f12c0c8 | ||
|
c9529ef9c1 | ||
|
7c6af583b1 | ||
|
cb7169b7de | ||
|
ed2f1ffbe9 | ||
|
edc63bbacb | ||
|
872fc75b58 | ||
|
089690768a | ||
|
aeca4e4211 | ||
|
1bf3578438 | ||
|
4611725fe9 | ||
|
c4f4de09f3 | ||
|
9af8abcb3c | ||
|
6687957025 | ||
|
320d5c46c1 | ||
|
7d55cff045 | ||
|
c22ed513cd | ||
|
cb4dcbfb95 | ||
|
9babfdf2ff | ||
|
2f97b78c4a | ||
|
48ce757df5 | ||
|
ddda963d95 | ||
|
b9371d8218 | ||
|
90f3e71d50 | ||
|
ba282ef8e2 | ||
|
0b41d9637d | ||
|
14bb3e35a7 | ||
|
a2fabff474 | ||
|
da9ec78a95 | ||
|
c1f764b7d1 | ||
|
a3a62be374 | ||
|
cb93cef889 | ||
|
9c65dd752a | ||
|
a280dd13a1 | ||
|
3b72a5e87f | ||
|
abc91ab501 | ||
|
b2a0123063 | ||
|
c3642d606f | ||
|
ebe69bf6bf | ||
|
7be0bface5 | ||
|
071e2a2dd9 | ||
|
c03df3bfb8 | ||
|
fa9fb276a2 | ||
|
5101a932ba | ||
|
7e76a4cb19 | ||
|
6c66cb36d3 | ||
|
bf2f0c13bb |
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,4 +18,3 @@ dist/
|
|||||||
*.ps
|
*.ps
|
||||||
*.toc
|
*.toc
|
||||||
*.gz
|
*.gz
|
||||||
index.html
|
|
||||||
|
10
README.md
10
README.md
@ -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.**
|
See also the [Getting Started Tutorial](https://github.com/flyingsparx/CENode/blob/master/docs/getting_started.md).
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ CENode can be imported into your Node apps or run in a browser. Either way, you
|
|||||||
|
|
||||||
Then add CENode to your project using NPM:
|
Then add CENode to your project using NPM:
|
||||||
```
|
```
|
||||||
npm install cenode
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
If using CENode in a webpage, then include it (and models, if necessary) in script tags:
|
If using CENode in a webpage, then include it (and models, if necessary) in script tags:
|
||||||
@ -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
|
||||||
|
@ -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 ~",
|
||||||
|
@ -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",
|
||||||
|
@ -43,9 +43,9 @@ class CEAgent {
|
|||||||
|
|
||||||
getInstance() {
|
getInstance() {
|
||||||
const instances = this.node.getInstances('agent');
|
const instances = this.node.getInstances('agent');
|
||||||
for (const instance of instances) {
|
for (let i = 0; i < instances.length; i += 1) {
|
||||||
if (instance.name.toLowerCase() === name.toLowerCase()) {
|
if (instances[i].name.toLowerCase() === name.toLowerCase()) {
|
||||||
return instance;
|
return instances[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
151
src/CEConcept.js
151
src/CEConcept.js
@ -19,14 +19,6 @@
|
|||||||
class CEConcept {
|
class CEConcept {
|
||||||
|
|
||||||
constructor(node, name, source) {
|
constructor(node, name, source) {
|
||||||
if (!name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const concept of node.concepts) {
|
|
||||||
if (concept.name.toLowerCase() === name.toLowerCase()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.id = node.newConceptId();
|
this.id = node.newConceptId();
|
||||||
@ -38,7 +30,6 @@ class CEConcept {
|
|||||||
node.concepts.push(this);
|
node.concepts.push(this);
|
||||||
this.node.conceptDict[this.id] = this;
|
this.node.conceptDict[this.id] = this;
|
||||||
|
|
||||||
if (isNaN(name[0])) {
|
|
||||||
const concept = this;
|
const concept = this;
|
||||||
Object.defineProperty(node.concepts, name.toLowerCase().replace(/ /g, '_'), {
|
Object.defineProperty(node.concepts, name.toLowerCase().replace(/ /g, '_'), {
|
||||||
get() {
|
get() {
|
||||||
@ -47,92 +38,91 @@ class CEConcept {
|
|||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
get instances() {
|
get instances() {
|
||||||
const array = [];
|
const instances = [];
|
||||||
for (const instance of this.node.instances) {
|
for (let i = 0; i < this.node.instances.length; i += 1) {
|
||||||
if (instance.concept.id === this.id) {
|
if (this.node.instances[i].type.id === this.id) {
|
||||||
array.push(instance);
|
instances.push(this.node.instances[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array;
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
get allInstances() {
|
get allInstances() {
|
||||||
const allConcepts = this.descendants.concat(this);
|
const allConcepts = this.descendants.concat(this);
|
||||||
const array = [];
|
const instances = [];
|
||||||
for (const instance of this.node.instances) {
|
for (let i = 0; i < this.node.instances.length; i += 1) {
|
||||||
for (const concept of allConcepts) {
|
for (let j = 0; j < allConcepts.length; j += 1) {
|
||||||
if (instance.concept.id === concept.id) {
|
if (this.node.instances[i].type.id === allConcepts[j].id) {
|
||||||
array.push(instance);
|
instances.push(this.node.instances[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array;
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
get parents() {
|
get parents() {
|
||||||
const array = [];
|
const p = [];
|
||||||
for (const id of this.parentIds) {
|
for (let i = 0; i < this.parentIds.length; i += 1) {
|
||||||
array.push(this.node.getConceptById(id));
|
p.push(this.node.getConceptById(this.parentIds[i]));
|
||||||
}
|
}
|
||||||
return array;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
get ancestors() {
|
get ancestors() {
|
||||||
const array = [];
|
const parents = [];
|
||||||
const stack = [];
|
const stack = [];
|
||||||
for (const parent of this.parents) {
|
for (let i = 0; i < this.parentIds.length; i += 1) {
|
||||||
stack.push(parent);
|
stack.push(this.parents[i]);
|
||||||
}
|
}
|
||||||
while (stack.length > 0) {
|
while (stack.length > 0) {
|
||||||
const current = stack.pop();
|
const current = stack.pop();
|
||||||
array.push(current);
|
parents.push(current);
|
||||||
for (const parent of current.parents) {
|
for (let i = 0; i < current.parents.length; i += 1) {
|
||||||
stack.push(parent);
|
stack.push(current.parents[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array;
|
return parents;
|
||||||
}
|
}
|
||||||
|
|
||||||
get children() {
|
get children() {
|
||||||
const array = [];
|
const children = [];
|
||||||
for (const concept of this.node.concepts) {
|
for (let i = 0; i < this.node.concepts.length; i += 1) {
|
||||||
for (const parent of concept.parents) {
|
for (let j = 0; j < this.node.concepts[i].parents.length; j += 1) {
|
||||||
if (parent.id === this.id) {
|
if (this.node.concepts[i].parents[j].id === this.id) {
|
||||||
array.push(concept);
|
children.push(this.node.concepts[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
get descendants() {
|
get descendants() {
|
||||||
const array = [];
|
const children = [];
|
||||||
const stack = [];
|
const stack = [];
|
||||||
for (const child of this.children) {
|
for (let i = 0; i < this.children.length; i += 1) {
|
||||||
stack.push(child);
|
stack.push(this.children[i]);
|
||||||
}
|
}
|
||||||
while (stack.length > 0) {
|
while (stack.length > 0) {
|
||||||
const current = stack.pop();
|
const current = stack.pop();
|
||||||
array.push(current);
|
children.push(current);
|
||||||
const currentChildren = current.children;
|
const currentChildren = current.children;
|
||||||
if (currentChildren) {
|
if (currentChildren) {
|
||||||
for (const child of currentChildren) {
|
for (let i = 0; i < currentChildren.length; i += 1) {
|
||||||
stack.push(child);
|
stack.push(currentChildren[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
get relationships() {
|
get relationships() {
|
||||||
const rels = [];
|
const rels = [];
|
||||||
for (const id of this.relationshipIds) {
|
for (let i = 0; i < this.relationshipIds.length; i += 1) {
|
||||||
const relationship = {};
|
const relationship = {};
|
||||||
relationship.label = id.label;
|
relationship.label = this.relationshipIds[i].label;
|
||||||
relationship.concept = this.node.getConceptById(id.target);
|
relationship.concept = this.node.getConceptById(this.relationshipIds[i].target);
|
||||||
rels.push(relationship);
|
rels.push(relationship);
|
||||||
}
|
}
|
||||||
return rels;
|
return rels;
|
||||||
@ -140,61 +130,60 @@ class CEConcept {
|
|||||||
|
|
||||||
get values() {
|
get values() {
|
||||||
const vals = [];
|
const vals = [];
|
||||||
for (const val of this.valueIds) {
|
for (let i = 0; i < this.valueIds.length; i += 1) {
|
||||||
const value = {};
|
const value = {};
|
||||||
value.label = val.label;
|
value.label = this.valueIds[i].label;
|
||||||
value.concept = val.type && this.node.getConceptById(val.type);
|
if (this.valueIds[i].conceptId === 0) {
|
||||||
|
value.concept = this.valueIds[i].typeName;
|
||||||
|
} else {
|
||||||
|
value.concept = this.node.getConceptById(this.valueIds[i].type);
|
||||||
|
}
|
||||||
vals.push(value);
|
vals.push(value);
|
||||||
}
|
}
|
||||||
return vals;
|
return vals;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCE(isModification) {
|
get ce() {
|
||||||
let ce = '';
|
let ce = `conceptualise a ~ ${this.name} ~ ${this.name.charAt(0).toUpperCase()}`;
|
||||||
if (isModification) {
|
if (this.parentIds.length > 0 || this.valueIds.length > 0 || this.relationshipIds.length > 0) {
|
||||||
ce += `conceptualise the ${this.name} ${this.name.charAt(0).toUpperCase()}`;
|
|
||||||
} else {
|
|
||||||
ce += `conceptualise a ~ ${this.name} ~ ${this.name.charAt(0).toUpperCase()}`;
|
|
||||||
}
|
|
||||||
if (!isModification && (this.parentIds.length > 0 || this.valueIds.length > 0 || this.relationshipIds.length > 0)) {
|
|
||||||
ce += ' that';
|
ce += ' that';
|
||||||
}
|
}
|
||||||
if (this.parentIds.length > 0) {
|
if (this.parentIds.length > 0) {
|
||||||
for (let i = 0; i < this.parents.length; i += 1) {
|
for (let i = 0; i < this.parentIds.length; i += 1) {
|
||||||
ce += ` is a ${this.parents[i].name}`;
|
ce += ` is a ${this.parentIds[i].name}`;
|
||||||
if (i < this.parents.length - 1) { ce += ' and'; }
|
if (i < this.parentIds.length - 1) { ce += ' and'; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const facts = [];
|
let facts = [];
|
||||||
const alph = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'];
|
const alph = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'];
|
||||||
for (let i = 0; i < this.valueIds.length; i += 1) {
|
for (let i = 0; i < this.valueIds.length; i += 1) {
|
||||||
if (this.valueIds[i].type === 0) {
|
if (this.valueIds[i].type === 0) {
|
||||||
facts.push(`has the value ${alph[i]} as ~ ${this.valueIds[i].label} ~`);
|
facts.push(`has the value ${alph[i]} as ${this.valueIds[i].label}`);
|
||||||
} else {
|
} else {
|
||||||
const valType = this.node.getConceptById(this.valueIds[i].type);
|
const valType = this.node.getConceptById(this.valueIds[i].type);
|
||||||
facts.push(`has the ${valType.name} ${valType.name.charAt(0).toUpperCase()} as ~ ${this.valueIds[i].label} ~`);
|
facts.push(`has the ${valType.name} ${valType.name.charAt(0).toUpperCase()} as ${this.valueIds[i].label}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let i = 0; i < this.relationshipIds.length; i += 1) {
|
|
||||||
const relType = this.node.getConceptById(this.relationshipIds[i].target);
|
|
||||||
facts.push(`~ ${this.relationshipIds[i].label} ~ the ${relType.name} ${alph[i]}`);
|
|
||||||
}
|
|
||||||
if (facts.length > 0) {
|
if (facts.length > 0) {
|
||||||
if (this.parentIds.length > 0) { ce += ' and'; }
|
if (this.parentIds.length > 0) { ce += ' and'; }
|
||||||
ce += ` ${facts.join(' and ')}`;
|
ce += ` ${facts.join(' and ')}`;
|
||||||
}
|
}
|
||||||
ce += '.';
|
ce += '.';
|
||||||
|
if (this.relationshipIds.length > 0) {
|
||||||
|
facts = [];
|
||||||
|
ce += `\nconceptualise the ${this.name} ${this.name.charAt(0).toUpperCase()}`;
|
||||||
|
for (let i = 0; i < this.relationshipIds.length; i += 1) {
|
||||||
|
const relType = this.node.getConceptById(this.relationshipIds[i].target);
|
||||||
|
facts.push(`~ ${this.relationshipIds[i].label} ~ the ${relType.name} ${alph[i]}`);
|
||||||
|
}
|
||||||
|
if (facts.length > 0) {
|
||||||
|
if (this.parentIds.length > 0 || this.valueIds.length > 0) { ce += ' and'; }
|
||||||
|
ce += ` ${facts.join(' and ')}.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
return ce;
|
return ce;
|
||||||
}
|
}
|
||||||
|
|
||||||
get creationCE() {
|
|
||||||
return `conceptualise a ~ ${this.name} ~ ${this.name.charAt(0).toUpperCase()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get ce() {
|
|
||||||
return this.getCE();
|
|
||||||
}
|
|
||||||
|
|
||||||
get gist() {
|
get gist() {
|
||||||
let gist = '';
|
let gist = '';
|
||||||
if (this.parentIds.length > 0) { gist += `A ${this.name}`; }
|
if (this.parentIds.length > 0) { gist += `A ${this.name}`; }
|
||||||
@ -230,7 +219,6 @@ class CEConcept {
|
|||||||
value.label = label;
|
value.label = label;
|
||||||
value.type = typeof type === 'number' ? type : type.id;
|
value.type = typeof type === 'number' ? type : type.id;
|
||||||
this.valueIds.push(value);
|
this.valueIds.push(value);
|
||||||
if (isNaN(label[0])) {
|
|
||||||
Object.defineProperty(this, label.toLowerCase().replace(/ /g, '_'), {
|
Object.defineProperty(this, label.toLowerCase().replace(/ /g, '_'), {
|
||||||
get() {
|
get() {
|
||||||
return type === 0 ? 'value' : type;
|
return type === 0 ? 'value' : type;
|
||||||
@ -238,7 +226,6 @@ class CEConcept {
|
|||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
addRelationship(label, target, source) {
|
addRelationship(label, target, source) {
|
||||||
const relationship = {};
|
const relationship = {};
|
||||||
@ -246,7 +233,6 @@ class CEConcept {
|
|||||||
relationship.label = label;
|
relationship.label = label;
|
||||||
relationship.target = target.id;
|
relationship.target = target.id;
|
||||||
this.relationshipIds.push(relationship);
|
this.relationshipIds.push(relationship);
|
||||||
if (isNaN(label[0])) {
|
|
||||||
Object.defineProperty(this, label.toLowerCase().replace(/ /g, '_'), {
|
Object.defineProperty(this, label.toLowerCase().replace(/ /g, '_'), {
|
||||||
get() {
|
get() {
|
||||||
return target;
|
return target;
|
||||||
@ -254,7 +240,6 @@ class CEConcept {
|
|||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
addParent(parentConcept) {
|
addParent(parentConcept) {
|
||||||
if (this.parentIds.indexOf(parentConcept.id) === -1) {
|
if (this.parentIds.indexOf(parentConcept.id) === -1) {
|
||||||
@ -263,8 +248,8 @@ class CEConcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addSynonym(synonym) {
|
addSynonym(synonym) {
|
||||||
for (const currentSynonym of this.synonyms) {
|
for (let i = 0; i < this.synonyms.length; i += 1) {
|
||||||
if (currentSynonym.toLowerCase() === synonym.toLowerCase()) {
|
if (this.synonyms[i].toLowerCase() === synonym.toLowerCase()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,14 +19,10 @@
|
|||||||
class CEInstance {
|
class CEInstance {
|
||||||
|
|
||||||
constructor(node, type, name, source) {
|
constructor(node, type, name, source) {
|
||||||
if (!type || !name) {
|
if (!type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const instance of node.instances) {
|
|
||||||
if (instance.name.toLowerCase() === name.toLowerCase() && type.id === instance.concept.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
@ -42,28 +38,26 @@ class CEInstance {
|
|||||||
node.instances.push(this);
|
node.instances.push(this);
|
||||||
this.node.instanceDict[this.id] = this;
|
this.node.instanceDict[this.id] = this;
|
||||||
|
|
||||||
if (isNaN(name[0])) {
|
|
||||||
const instance = this;
|
const instance = this;
|
||||||
const helperName = name.toLowerCase().replace(/ /g, '_').replace(/'/g, '');
|
Object.defineProperty(node.instances, name.toLowerCase().replace(/ /g, '_').replace(/'/g, ''), {
|
||||||
Object.defineProperty(node.instances, helperName, {
|
|
||||||
get() {
|
get() {
|
||||||
return instance;
|
return instance;
|
||||||
},
|
},
|
||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
Object.defineProperty(type, helperName, {
|
|
||||||
|
Object.defineProperty(type, name.toLowerCase(), {
|
||||||
get() {
|
get() {
|
||||||
return instance;
|
return instance;
|
||||||
},
|
},
|
||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
get type() {
|
get type() {
|
||||||
for (const concept of this.node.concepts) {
|
for (let i = 0; i < this.node.concepts.length; i += 1) {
|
||||||
if (concept.id === this.concept.id) {
|
if (this.node.concepts[i].id === this.concept.id) {
|
||||||
return concept;
|
return this.node.concepts[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -71,11 +65,11 @@ class CEInstance {
|
|||||||
|
|
||||||
get relationships() {
|
get relationships() {
|
||||||
const rels = [];
|
const rels = [];
|
||||||
for (const id of this.relationshipIds) {
|
for (let i = 0; i < this.relationshipIds.length; i += 1) {
|
||||||
const relationship = {};
|
const relationship = {};
|
||||||
relationship.label = id.label;
|
relationship.label = this.relationshipIds[i].label;
|
||||||
relationship.source = id.source;
|
relationship.source = this.relationshipIds[i].source;
|
||||||
relationship.instance = this.node.getInstanceById(id.targetId);
|
relationship.instance = this.node.getInstanceById(this.relationshipIds[i].targetId);
|
||||||
rels.push(relationship);
|
rels.push(relationship);
|
||||||
}
|
}
|
||||||
return rels;
|
return rels;
|
||||||
@ -83,14 +77,14 @@ class CEInstance {
|
|||||||
|
|
||||||
get values() {
|
get values() {
|
||||||
const vals = [];
|
const vals = [];
|
||||||
for (const id of this.valueIds) {
|
for (let i = 0; i < this.valueIds.length; i += 1) {
|
||||||
const value = {};
|
const value = {};
|
||||||
value.label = id.label;
|
value.label = this.valueIds[i].label;
|
||||||
value.source = id.source;
|
value.source = this.valueIds[i].source;
|
||||||
if (id.conceptId === 0) {
|
if (this.valueIds[i].conceptId === 0) {
|
||||||
value.instance = id.typeName;
|
value.instance = this.valueIds[i].typeName;
|
||||||
} else {
|
} else {
|
||||||
value.instance = this.node.getInstanceById(id.conceptId);
|
value.instance = this.node.getInstanceById(this.valueIds[i].conceptId);
|
||||||
}
|
}
|
||||||
vals.push(value);
|
vals.push(value);
|
||||||
}
|
}
|
||||||
@ -102,19 +96,15 @@ class CEInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPossibleProperties() {
|
getPossibleProperties() {
|
||||||
let ancestorInstances = this.concept.ancestors;
|
const ancestorInstances = this.concept.ancestors;
|
||||||
ancestorInstances.push(this.concept);
|
ancestorInstances.push(this.concept);
|
||||||
for (const subConcept of this.subConcepts) {
|
|
||||||
ancestorInstances.push(subConcept);
|
|
||||||
ancestorInstances = ancestorInstances.concat(subConcept.ancestors);
|
|
||||||
}
|
|
||||||
const properties = { values: [], relationships: [] };
|
const properties = { values: [], relationships: [] };
|
||||||
for (const ancestor of ancestorInstances) {
|
for (let i = 0; i < ancestorInstances.length; i += 1) {
|
||||||
for (const value of ancestor.values) {
|
for (let j = 0; j < ancestorInstances[i].values.length; j += 1) {
|
||||||
properties.values.push(value.label.toLowerCase());
|
properties.values.push(ancestorInstances[i].values[j].label.toLowerCase());
|
||||||
}
|
}
|
||||||
for (const relationship of ancestor.relationships) {
|
for (let j = 0; j < ancestorInstances[i].relationships.length; j += 1) {
|
||||||
properties.relationships.push(relationship.label.toLowerCase());
|
properties.relationships.push(ancestorInstances[i].relationships[j].label.toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return properties;
|
return properties;
|
||||||
@ -133,7 +123,7 @@ class CEInstance {
|
|||||||
this.valueIds.push(value);
|
this.valueIds.push(value);
|
||||||
const valueNameField = label.toLowerCase().replace(/ /g, '_');
|
const valueNameField = label.toLowerCase().replace(/ /g, '_');
|
||||||
|
|
||||||
if (this.reservedFields.indexOf(valueNameField) === -1 && isNaN(valueNameField[0])) {
|
if (this.reservedFields.indexOf(valueNameField) === -1) {
|
||||||
Object.defineProperty(this, valueNameField, {
|
Object.defineProperty(this, valueNameField, {
|
||||||
get() {
|
get() {
|
||||||
return value.conceptId === 0 ? value.typeName : this.node.getInstanceById(value.conceptId);
|
return value.conceptId === 0 ? value.typeName : this.node.getInstanceById(value.conceptId);
|
||||||
@ -145,9 +135,9 @@ class CEInstance {
|
|||||||
Object.defineProperty(this, `${valueNameField}s`, {
|
Object.defineProperty(this, `${valueNameField}s`, {
|
||||||
get() {
|
get() {
|
||||||
const instances = [];
|
const instances = [];
|
||||||
for (const id of this.valueIds) {
|
for (let i = 0; i < this.valueIds.length; i += 1) {
|
||||||
if (id.label.toLowerCase().replace(/ /g, '_') === valueNameField) {
|
if (this.valueIds[i].label.toLowerCase().replace(/ /g, '_') === valueNameField) {
|
||||||
instances.push(id.conceptId === 0 ? id.typeName : this.node.getInstanceById(id.conceptId));
|
instances.push(this.valueIds[i].conceptId === 0 ? this.valueIds[i].typeName : this.node.getInstanceById(this.valueIds[i].conceptId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return instances;
|
return instances;
|
||||||
@ -172,7 +162,7 @@ class CEInstance {
|
|||||||
this.relationshipIds.push(relationship);
|
this.relationshipIds.push(relationship);
|
||||||
const relNameField = label.toLowerCase().replace(/ /g, '_');
|
const relNameField = label.toLowerCase().replace(/ /g, '_');
|
||||||
|
|
||||||
if (this.reservedFields.indexOf(relNameField) === -1 && isNaN(relNameField[0])) {
|
if (this.reservedFields.indexOf(relNameField) === -1) {
|
||||||
Object.defineProperty(this, relNameField, {
|
Object.defineProperty(this, relNameField, {
|
||||||
get() {
|
get() {
|
||||||
return this.node.getInstanceById(relationship.targetId);
|
return this.node.getInstanceById(relationship.targetId);
|
||||||
@ -184,9 +174,9 @@ class CEInstance {
|
|||||||
Object.defineProperty(this, `${relNameField}s`, {
|
Object.defineProperty(this, `${relNameField}s`, {
|
||||||
get() {
|
get() {
|
||||||
const instances = [];
|
const instances = [];
|
||||||
for (const id of this.relationshipIds) {
|
for (let i = 0; i < this.relationshipIds.length; i += 1) {
|
||||||
if (id.label.toLowerCase().replace(/ /g, '_') === relNameField) {
|
if (this.relationshipIds[i].label.toLowerCase().replace(/ /g, '_') === relNameField) {
|
||||||
instances.push(this.node.getInstanceById(id.targetId));
|
instances.push(this.node.getInstanceById(this.relationshipIds[i].targetId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return instances;
|
return instances;
|
||||||
@ -205,19 +195,17 @@ class CEInstance {
|
|||||||
if (!synonym || !synonym.length) {
|
if (!synonym || !synonym.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (const checkSynonym of this.synonyms) {
|
for (let i = 0; i < this.synonyms.length; i += 1) {
|
||||||
if (checkSynonym.toLowerCase() === synonym.toLowerCase()) {
|
if (this.synonyms[i].toLowerCase() === synonym.toLowerCase()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.synonyms.push(synonym);
|
this.synonyms.push(synonym);
|
||||||
if (isNaN(synonym[0])) {
|
|
||||||
Object.defineProperty(this, synonym.toLowerCase().replace(/ /g, '_'), {
|
Object.defineProperty(this, synonym.toLowerCase().replace(/ /g, '_'), {
|
||||||
get() {
|
get() {
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,79 +250,69 @@ class CEInstance {
|
|||||||
return onlyOne ? null : properties;
|
return onlyOne ? null : properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCE(isModification) {
|
get ce() {
|
||||||
const concept = this.concept;
|
const concept = this.concept;
|
||||||
if (!concept) { return ''; }
|
if (!concept) { return ''; }
|
||||||
|
let ce = `there is a ${concept.name} named '${this.name}'`;
|
||||||
let ce = '';
|
|
||||||
if (isModification) {
|
|
||||||
ce += `the ${concept.name} '${this.name}'`;
|
|
||||||
} else {
|
|
||||||
ce += `there is a ${concept.name} named '${this.name}'`;
|
|
||||||
}
|
|
||||||
const facts = [];
|
const facts = [];
|
||||||
for (const subConcept of this.subConcepts) {
|
for (const subConcept of this.subConcepts) {
|
||||||
facts.push(`is a ${subConcept.name}`);
|
facts.push(`is a ${subConcept.name}`);
|
||||||
}
|
}
|
||||||
for (const id of this.valueIds) {
|
for (let i = 0; i < this.valueIds.length; i += 1) {
|
||||||
if (id.conceptId === 0) {
|
const value = this.valueIds[i];
|
||||||
facts.push(`has '${id.typeName.replace(/'/g, "\\'")}' as ${id.label}`);
|
if (value.conceptId === 0) {
|
||||||
|
facts.push(`has '${value.typeName.replace(/'/g, "\\'")}' as ${value.label}`);
|
||||||
} else {
|
} else {
|
||||||
const valueInstance = this.node.getInstanceById(id.conceptId);
|
const valueInstance = this.node.getInstanceById(value.conceptId);
|
||||||
const valueConcept = valueInstance.type;
|
const valueConcept = valueInstance.type;
|
||||||
facts.push(`has the ${valueConcept.name} '${valueInstance.name}' as ${id.label}`);
|
facts.push(`has the ${valueConcept.name} '${valueInstance.name}' as ${value.label}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const id of this.relationshipIds) {
|
for (let i = 0; i < this.relationshipIds.length; i += 1) {
|
||||||
const relationshipInstance = this.node.getInstanceById(id.targetId);
|
const relationship = this.relationshipIds[i];
|
||||||
|
const relationshipInstance = this.node.getInstanceById(relationship.targetId);
|
||||||
const relationshipConcept = relationshipInstance.type;
|
const relationshipConcept = relationshipInstance.type;
|
||||||
facts.push(`${id.label} the ${relationshipConcept.name} '${relationshipInstance.name}'`);
|
facts.push(`${relationship.label} the ${relationshipConcept.name} '${relationshipInstance.name}'`);
|
||||||
}
|
}
|
||||||
if (facts.length > 0) { ce += `${!isModification && ' that'} ${facts.join(' and ')}`; }
|
if (facts.length > 0) { ce += ` that ${facts.join(' and ')}`; }
|
||||||
return `${ce}.`;
|
return `${ce}.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get creationCE() {
|
|
||||||
return `there is a ${this.concept && this.concept.name} named '${this.name}'`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get ce() {
|
|
||||||
return this.getCE();
|
|
||||||
}
|
|
||||||
|
|
||||||
get gist() {
|
get gist() {
|
||||||
const vowels = ['a', 'e', 'i', 'o', 'u'];
|
const vowels = ['a', 'e', 'i', 'o', 'u'];
|
||||||
const concept = this.concept;
|
const concept = this.concept;
|
||||||
if (!concept) { return ''; }
|
if (!concept) { return ''; }
|
||||||
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 (let i = 0; i < this.subConcepts.length; i += 1) {
|
||||||
gist += ` and ${vowels.indexOf(subConcept.name.toLowerCase()[0]) > -1 ? 'an' : 'a'} ${subConcept.name}`;
|
gist += ` and a ${this.subConcepts[i].name}`;
|
||||||
}
|
}
|
||||||
gist += '.';
|
gist += '.';
|
||||||
|
|
||||||
const facts = {};
|
const facts = {};
|
||||||
let factFound = false;
|
let factFound = false;
|
||||||
for (const id of this.valueIds) {
|
for (let i = 0; i < this.valueIds.length; i += 1) {
|
||||||
factFound = true;
|
factFound = true;
|
||||||
|
const value = this.valueIds[i];
|
||||||
let fact = '';
|
let fact = '';
|
||||||
if (id.conceptId === 0) {
|
if (value.conceptId === 0) {
|
||||||
fact = `has '${id.typeName.replace(/'/g, "\\'")}' as ${id.label}`;
|
fact = `has '${value.typeName.replace(/'/g, "\\'")}' as ${value.label}`;
|
||||||
} else {
|
} else {
|
||||||
const valueInstance = this.node.getInstanceById(id.conceptId);
|
const valueInstance = this.node.getInstanceById(value.conceptId);
|
||||||
const valueConcept = valueInstance.type;
|
const valueConcept = valueInstance.type;
|
||||||
fact = `has the ${valueConcept.name} '${valueInstance.name}' as ${id.label}`;
|
fact = `has the ${valueConcept.name} '${valueInstance.name}' as ${value.label}`;
|
||||||
}
|
}
|
||||||
if (!(fact in facts)) {
|
if (!(fact in facts)) {
|
||||||
facts[fact] = 0;
|
facts[fact] = 0;
|
||||||
}
|
}
|
||||||
facts[fact] += 1;
|
facts[fact] += 1;
|
||||||
}
|
}
|
||||||
for (const id of this.relationshipIds) {
|
for (let i = 0; i < this.relationshipIds.length; i += 1) {
|
||||||
factFound = true;
|
factFound = true;
|
||||||
const relationshipInstance = this.node.getInstanceById(id.targetId);
|
const relationship = this.relationshipIds[i];
|
||||||
|
const relationshipInstance = this.node.getInstanceById(relationship.targetId);
|
||||||
const relationshipConcept = relationshipInstance.type;
|
const relationshipConcept = relationshipInstance.type;
|
||||||
const fact = `${id.label} the ${relationshipConcept.name} '${relationshipInstance.name}'`;
|
const fact = `${relationship.label} the ${relationshipConcept.name} '${relationshipInstance.name}'`;
|
||||||
if (!(fact in facts)) {
|
if (!(fact in facts)) {
|
||||||
facts[fact] = 0;
|
facts[fact] = 0;
|
||||||
}
|
}
|
||||||
|
@ -46,13 +46,13 @@ class CENode {
|
|||||||
|
|
||||||
getConceptByName(name) {
|
getConceptByName(name) {
|
||||||
if (!name) { return null; }
|
if (!name) { return null; }
|
||||||
for (const concept of this.concepts) {
|
for (let i = 0; i < this.concepts.length; i += 1) {
|
||||||
if (concept.name.toLowerCase() === name.toLowerCase()) {
|
if (this.concepts[i].name.toLowerCase() === name.toLowerCase()) {
|
||||||
return concept;
|
return this.concepts[i];
|
||||||
}
|
}
|
||||||
for (const synonym of concept.synonyms) {
|
for (let j = 0; j < this.concepts[i].synonyms.length; j += 1) {
|
||||||
if (synonym.toLowerCase() === name.toLowerCase()) {
|
if (this.concepts[i].synonyms[j].toLowerCase() === name.toLowerCase()) {
|
||||||
return concept;
|
return this.concepts[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,18 +63,15 @@ class CENode {
|
|||||||
return this.instanceDict[id];
|
return this.instanceDict[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
getInstanceByName(name, concept) {
|
getInstanceByName(name) {
|
||||||
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 (let i = 0; i < this.instances.length; i += 1) {
|
||||||
for (const instance of this.instances) {
|
if (this.instances[i].name.toLowerCase() === name.toLowerCase()) {
|
||||||
if (instance && (concept ? possibleConcepts.indexOf(instance.concept.id) > -1 : true)) {
|
return this.instances[i];
|
||||||
if (instance.name.toLowerCase() === name.toLowerCase()) {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
for (const synonym of instance.synonyms) {
|
|
||||||
if (synonym.toLowerCase() === name.toLowerCase()) {
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
for (let j = 0; j < this.instances[i].synonyms.length; j += 1) {
|
||||||
|
if (this.instances[i].synonyms[j].toLowerCase() === name.toLowerCase()) {
|
||||||
|
return this.instances[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,17 +95,15 @@ class CENode {
|
|||||||
* Returns: [obj{instance}]
|
* Returns: [obj{instance}]
|
||||||
*/
|
*/
|
||||||
getInstances(conceptType, recurse) {
|
getInstances(conceptType, recurse) {
|
||||||
const instanceList = [];
|
let instanceList = [];
|
||||||
if (!conceptType) {
|
if (!conceptType) {
|
||||||
for (const instance of this.instances) {
|
instanceList = this.instances;
|
||||||
instanceList.push(instance);
|
|
||||||
}
|
|
||||||
} else if (conceptType && !recurse) {
|
} else if (conceptType && !recurse) {
|
||||||
const concept = this.getConceptByName(conceptType);
|
const concept = this.getConceptByName(conceptType);
|
||||||
if (concept) {
|
if (concept) {
|
||||||
for (const instance of this.instances) {
|
for (let i = 0; i < this.instances.length; i += 1) {
|
||||||
if (instance && instance.concept.id === concept.id) {
|
if (this.instances[i].type.id === concept.id) {
|
||||||
instanceList.push(instance);
|
instanceList.push(this.instances[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,10 +112,10 @@ class CENode {
|
|||||||
if (concept) {
|
if (concept) {
|
||||||
const descendants = concept.descendants.concat(concept);
|
const descendants = concept.descendants.concat(concept);
|
||||||
const childrenIds = [];
|
const childrenIds = [];
|
||||||
for (const descendant of descendants) { childrenIds.push(descendant.id); }
|
for (let i = 0; i < descendants.length; i += 1) { childrenIds.push(descendants[i].id); }
|
||||||
for (const instance of this.instances) {
|
for (let i = 0; i < this.instances.length; i += 1) {
|
||||||
if (instance && childrenIds.indexOf(instance.concept.id) > -1) {
|
if (childrenIds.indexOf(this.instances[i].type.id) > -1) {
|
||||||
instanceList.push(instance);
|
instanceList.push(this.instances[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,12 +132,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,8 +151,8 @@ class CENode {
|
|||||||
*/
|
*/
|
||||||
addSentences(sentences, source) {
|
addSentences(sentences, source) {
|
||||||
const responses = [];
|
const responses = [];
|
||||||
for (const sentence of sentences) {
|
for (let i = 0; i < sentences.length; i += 1) {
|
||||||
responses.push(this.addSentence(sentence, source));
|
responses.push(this.addSentence(sentences[i], source));
|
||||||
}
|
}
|
||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
@ -170,7 +165,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 +181,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 +196,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],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -200,10 +210,8 @@ class CENode {
|
|||||||
*/
|
*/
|
||||||
loadModel(sentences) {
|
loadModel(sentences) {
|
||||||
const responses = [];
|
const responses = [];
|
||||||
if (sentences && sentences.length) {
|
for (let i = 0; i < sentences.length; i += 1) {
|
||||||
for (const sentence of sentences) {
|
responses.push(this.addCE(sentences[i]));
|
||||||
responses.push(this.addCE(sentence));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
@ -242,8 +250,8 @@ class CENode {
|
|||||||
this.lastInstanceId = this.instances.length;
|
this.lastInstanceId = this.instances.length;
|
||||||
this.lastConceptId = this.concepts.length;
|
this.lastConceptId = this.concepts.length;
|
||||||
this.lastCardId = 0;
|
this.lastCardId = 0;
|
||||||
for (const model of models) {
|
for (let i = 0; i < models.length; i += 1) {
|
||||||
this.loadModel(model);
|
this.loadModel(models[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,22 +42,17 @@ 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/i)) {
|
||||||
return this.modifyConcept(t, source);
|
return this.modifyConcept(t, source);
|
||||||
} else if (t.match(/^there is an? ([a-zA-Z0-9 ]*) named/i)) {
|
} else if (t.match(/^there is an? ([a-zA-Z0-9 ]*) named/i)) {
|
||||||
return this.newInstance(t, source);
|
return this.newInstance(t, source);
|
||||||
} 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,16 @@ 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) {
|
|
||||||
return CEParser.error('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 +88,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,19 +132,19 @@ 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);
|
||||||
|
const instance = new CEInstance(this.node, concept, instanceName, source);
|
||||||
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 [true, 'There is already an instance of this type with this name.', currentInstance];
|
||||||
}
|
}
|
||||||
const instance = new CEInstance(this.node, concept, instanceName, source);
|
|
||||||
instance.sentences.push(t);
|
instance.sentences.push(t);
|
||||||
|
|
||||||
const remainder = t.replace(/^there is an? (?:[a-zA-Z0-9 ]*) named (?:[a-zA-Z0-9_]*|'[a-zA-Z0-9_ ]*') that/, '');
|
const remainder = t.replace(/^there is an? (?:[a-zA-Z0-9 ]*) named (?:[a-zA-Z0-9_]*|'[a-zA-Z0-9_ ]*') that/, '');
|
||||||
@ -168,47 +152,42 @@ 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;
|
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]);
|
||||||
|
instance = this.node.getInstanceByName(names[2].replace(/\\/g, ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 (let i = 0; i < this.node.concepts.length; i += 1) {
|
||||||
if (names[1].toLowerCase().indexOf(conceptCheck.name.toLowerCase()) === 0) {
|
if (names[1].toLowerCase().indexOf(this.node.concepts[i].name.toLowerCase()) === 0) {
|
||||||
concept = conceptCheck;
|
concept = this.node.concepts[i];
|
||||||
instanceName = nameTokens[concept.name.split(' ').length];
|
const instanceName = nameTokens[concept.name.split(' ').length].toLowerCase();
|
||||||
instance = this.node.getInstanceByName(instanceName, concept);
|
instance = concept[instanceName];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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(' ');
|
|
||||||
tokens.splice(0, 1 + concept.name.split(' ').length + instanceName.split(' ').length);
|
const remainder = t.replace(`'${instance.name}'`, instance.name).replace(`the ${concept.name} ${instance.name}`.trim(), '');
|
||||||
const remainder = tokens.join(' ');
|
|
||||||
const facts = remainder.replace(/\band\b/g, '+').match(/(?:'(?:\\.|[^'])*'|[^+])+/g);
|
const facts = remainder.replace(/\band\b/g, '+').match(/(?:'(?:\\.|[^'])*'|[^+])+/g);
|
||||||
if (facts) {
|
|
||||||
for (const fact of facts) {
|
for (const fact of facts) {
|
||||||
this.processInstanceFact(instance, fact, source);
|
this.processInstanceFact(instance, fact, source);
|
||||||
}
|
}
|
||||||
}
|
return [true, t, instance];
|
||||||
return CEParser.success(t, instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processInstanceFact(instance, fact, source) {
|
processInstanceFact(instance, fact, source) {
|
||||||
@ -220,15 +199,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);
|
||||||
|
let relInstance = this.node.getInstanceByName(relInstanceName);
|
||||||
if (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 +219,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);
|
||||||
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);
|
||||||
|
362
src/CEServer.js
362
src/CEServer.js
@ -20,277 +20,141 @@ const http = require('http');
|
|||||||
const CENode = require('./CENode.js');
|
const CENode = require('./CENode.js');
|
||||||
const CEModels = require('../models/index.js');
|
const CEModels = require('../models/index.js');
|
||||||
|
|
||||||
class CEServer {
|
const POST_SENTENCES_ENDPOINT = '/sentences';
|
||||||
|
const GET_CARDS_ENDPOINT = '/cards';
|
||||||
|
|
||||||
constructor(name, port, models) {
|
function postSentences(node, request, response) {
|
||||||
this.port = port;
|
|
||||||
this.node = new CENode();
|
|
||||||
if (models) {
|
|
||||||
for (const model of models) {
|
|
||||||
this.node.loadModel(CEModels[model]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.node.attachAgent();
|
|
||||||
this.node.agent.setName(name);
|
|
||||||
this.handlers = {
|
|
||||||
GET: {
|
|
||||||
'/cards': (request, response) => {
|
|
||||||
const agentRegex = decodeURIComponent(request.url).match(/agent=(.*)/);
|
|
||||||
const agentStr = agentRegex ? agentRegex[1] : null;
|
|
||||||
const agents = (agentStr && agentStr.toLowerCase().split(',')) || [];
|
|
||||||
let s = '';
|
|
||||||
for (const card of this.node.getInstances('card', true)) {
|
|
||||||
for (const to of card.is_tos) {
|
|
||||||
for (const agent of agents) {
|
|
||||||
if (to.name.toLowerCase() === agent) {
|
|
||||||
s += `${card.ce}\n`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response.writeHead(200, { 'Content-Type': 'text/ce' });
|
|
||||||
response.end(s);
|
|
||||||
},
|
|
||||||
'/concepts': (request, response) => {
|
|
||||||
const concepts = [];
|
|
||||||
for (const concept of this.node.concepts) {
|
|
||||||
concepts.push({
|
|
||||||
name: concept.name,
|
|
||||||
id: concept.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
||||||
response.end(JSON.stringify(concepts));
|
|
||||||
},
|
|
||||||
'/concept': (request, response) => {
|
|
||||||
const idRegex = decodeURIComponent(request.url).match(/id=(.*)/);
|
|
||||||
const id = idRegex ? idRegex[1] : null;
|
|
||||||
const concept = this.node.getConceptById(id);
|
|
||||||
if (concept) {
|
|
||||||
const body = { name: concept.name, ce: concept.ce, parents: [], children: [], instances: [], values: [], relationships: [] };
|
|
||||||
for (const parent of concept.parents) {
|
|
||||||
body.parents.push({
|
|
||||||
name: parent.name,
|
|
||||||
id: parent.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (const child of concept.children) {
|
|
||||||
body.children.push({
|
|
||||||
name: child.name,
|
|
||||||
id: child.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (const instance of concept.instances) {
|
|
||||||
body.instances.push({
|
|
||||||
name: instance.name,
|
|
||||||
id: instance.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (const value of concept.values) {
|
|
||||||
const valueName = value.concept && value.concept.name;
|
|
||||||
const valueId = value.concept && value.concept.id;
|
|
||||||
body.values.push({ label: value.label, targetName: valueName, targetId: valueId });
|
|
||||||
}
|
|
||||||
for (const relationship of concept.relationships) {
|
|
||||||
body.relationships.push({ label: relationship.label, targetName: relationship.concept.name, targetId: relationship.concept.id });
|
|
||||||
}
|
|
||||||
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
||||||
return response.end(JSON.stringify(body));
|
|
||||||
}
|
|
||||||
response.writeHead(404);
|
|
||||||
return response.end('Concept not found');
|
|
||||||
},
|
|
||||||
'/instances': (request, response) => {
|
|
||||||
const instances = [];
|
|
||||||
for (const instance of this.node.instances) {
|
|
||||||
instances.push({
|
|
||||||
name: instance.name,
|
|
||||||
id: instance.id,
|
|
||||||
conceptName: instance.concept.name,
|
|
||||||
conceptId: instance.concept.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
||||||
response.end(JSON.stringify(instances));
|
|
||||||
},
|
|
||||||
'/instance': (request, response) => {
|
|
||||||
const idRegex = decodeURIComponent(request.url).match(/id=(.*)/);
|
|
||||||
const nameRegex = decodeURIComponent(request.url).match(/name=(.*)/);
|
|
||||||
const idQuery = idRegex ? idRegex[1] : null;
|
|
||||||
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) {
|
|
||||||
const body = {
|
|
||||||
name: instance.name,
|
|
||||||
conceptName: instance.concept.name,
|
|
||||||
conceptId: instance.concept.id,
|
|
||||||
ce: instance.ce,
|
|
||||||
gist: instance.gist,
|
|
||||||
synonyms: instance.synonyms,
|
|
||||||
subConcepts: [],
|
|
||||||
values: [],
|
|
||||||
relationships: [],
|
|
||||||
};
|
|
||||||
for (const concept of instance.subConcepts) {
|
|
||||||
body.subConcepts.push({ name: concept.name, id: concept.id });
|
|
||||||
}
|
|
||||||
for (const value of instance.values) {
|
|
||||||
const valueName = value.instance.name || value.instance;
|
|
||||||
const valueId = value.instance.id;
|
|
||||||
const conceptName = value.instance.concept && value.instance.concept.name;
|
|
||||||
const conceptId = value.instance.concept && value.instance.concept.id;
|
|
||||||
body.values.push({ label: value.label, targetName: valueName, targetId: valueId, targetConceptName: conceptName, targetConceptId: conceptId });
|
|
||||||
}
|
|
||||||
for (const relationship of instance.relationships) {
|
|
||||||
body.relationships.push({ label: relationship.label, targetName: relationship.instance.name, targetId: relationship.instance.id, targetConceptName: relationship.instance.concept.name, targetConceptId: relationship.instance.concept.id });
|
|
||||||
}
|
|
||||||
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
||||||
return response.end(JSON.stringify(body));
|
|
||||||
}
|
|
||||||
response.writeHead(404);
|
|
||||||
return response.end('Unable to find the instance.');
|
|
||||||
},
|
|
||||||
'/info': (request, response) => {
|
|
||||||
const body = { recentInstances: [], recentConcepts: [], instanceCount: this.node.instances.length, conceptCount: this.node.concepts.length };
|
|
||||||
const recentInstances = this.node.instances.slice(this.node.instances.length >= 10 ? this.node.instances.length - 10 : 0);
|
|
||||||
for (const instance of recentInstances) {
|
|
||||||
body.recentInstances.push({
|
|
||||||
name: instance.name,
|
|
||||||
id: instance.id,
|
|
||||||
conceptName: instance.concept.name,
|
|
||||||
conceptId: instance.concept.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (const concept of this.node.concepts) {
|
|
||||||
body.recentConcepts.push({
|
|
||||||
name: concept.name,
|
|
||||||
id: concept.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
||||||
response.end(JSON.stringify(body));
|
|
||||||
},
|
|
||||||
'/model': (request, response) => {
|
|
||||||
let body = '';
|
|
||||||
for (const concept of this.node.concepts) { body += `${concept.creationCE}\n`; }
|
|
||||||
for (const concept of this.node.concepts) { body += `${concept.getCE(true)}\n`; }
|
|
||||||
for (const instance of this.node.instances) { body += `${instance.creationCE}\n`; }
|
|
||||||
for (const instance of this.node.instances) { body += `${instance.getCE(true)}\n`; }
|
|
||||||
response.writeHead(200, { 'Content-Type': 'text/ce', 'Content-Disposition': `attachment; filename="${this.node.agent.name}.ce"` });
|
|
||||||
response.end(body);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
POST: {
|
|
||||||
'/cards': (request, response) => {
|
|
||||||
let body = '';
|
|
||||||
request.on('data', (chunk) => { body += chunk; });
|
|
||||||
request.on('end', () => {
|
|
||||||
const ignores = body.split(/\\n|\n/);
|
|
||||||
const agentRegex = decodeURIComponent(request.url).match(/agent=(.*)/);
|
|
||||||
const agentStr = agentRegex ? agentRegex[1] : null;
|
|
||||||
const agents = (agentStr && agentStr.toLowerCase().split(',')) || [];
|
|
||||||
let s = '';
|
|
||||||
for (const card of this.node.getInstances('card', true)) {
|
|
||||||
if (ignores.indexOf(card.name) === -1) {
|
|
||||||
if (agents.length === 0) {
|
|
||||||
s += `${card.ce}\n`;
|
|
||||||
} else if (card.is_tos) {
|
|
||||||
for (const to of card.is_tos) {
|
|
||||||
for (const agent of agents) {
|
|
||||||
if (to.name.toLowerCase() === agent) {
|
|
||||||
s += `${card.ce}\n`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response.writeHead(200, { 'Content-Type': 'text/ce' });
|
|
||||||
response.end(s);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'/sentences': (request, response) => {
|
|
||||||
let body = '';
|
let body = '';
|
||||||
request.on('data', (chunk) => { body += chunk; });
|
request.on('data', (chunk) => { body += chunk; });
|
||||||
request.on('end', () => {
|
request.on('end', () => {
|
||||||
body = decodeURIComponent(body.replace('sentence=', '').replace(/\+/g, ' '));
|
body = decodeURIComponent(body.replace('sentence=', '').replace(/\+/g, ' '));
|
||||||
const sentences = body.split(/\\n|\n/);
|
const sentences = body.split(/\\n|\n/);
|
||||||
const responses = this.node.addSentences(sentences);
|
const responses = node.addSentences(sentences);
|
||||||
response.writeHead(200, { 'Content-Type': 'text/ce' });
|
response.write(responses.map(resp => resp.data).join('\n'));
|
||||||
response.end(responses.map(resp => resp.data).join('\n'));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PUT: {
|
|
||||||
'/reset': (request, response) => {
|
|
||||||
this.node.resetAll();
|
|
||||||
response.writeHead(204);
|
|
||||||
response.end();
|
response.end();
|
||||||
},
|
});
|
||||||
'/agent/name': (request, response) => {
|
}
|
||||||
|
|
||||||
|
function getCards(node, request, response, ignoresInput) {
|
||||||
|
const url = decodeURIComponent(request.url);
|
||||||
|
const agentRegex = url.match(/agent=(.*)/);
|
||||||
|
const ignores = ignoresInput || [];
|
||||||
|
let agentStr = null;
|
||||||
|
let agents = [];
|
||||||
|
if (agentRegex) { agentStr = agentRegex[1]; }
|
||||||
|
if (agentStr) {
|
||||||
|
agents = agentStr.toLowerCase().split(',');
|
||||||
|
}
|
||||||
|
const cards = node.getInstances('card', true);
|
||||||
|
let s = '';
|
||||||
|
for (let i = 0; i < cards.length; i += 1) {
|
||||||
|
if (ignores.indexOf(cards[i].name) === -1) {
|
||||||
|
if (!agents || agents.length === 0) {
|
||||||
|
s += `${cards[i].ce}\n`;
|
||||||
|
} else {
|
||||||
|
const tos = cards[i].is_tos;
|
||||||
|
if (tos) {
|
||||||
|
for (let j = 0; j < tos.length; j += 1) {
|
||||||
|
for (let k = 0; k < agents.length; k += 1) {
|
||||||
|
if (tos[j].name.toLowerCase() === agents[k]) {
|
||||||
|
s += `${cards[i].ce}\n`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.write(s);
|
||||||
|
response.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startServer() {
|
||||||
|
const node = new CENode(CEModels.core, CEModels.server);
|
||||||
|
node.attachAgent();
|
||||||
|
let port = 5555;
|
||||||
|
if (process.argv.length > 3) {
|
||||||
|
port = process.argv[3];
|
||||||
|
}
|
||||||
|
if (process.argv.length > 2) {
|
||||||
|
node.agent.setName(process.argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
http.createServer((request, response) => {
|
||||||
|
response.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
|
||||||
|
if (request.method === 'GET') {
|
||||||
|
if (request.url === '/') {
|
||||||
|
const ins = node.instances;
|
||||||
|
const con = node.concepts;
|
||||||
|
let s = '<html><head><title>CENode Management</title></head><body><h1>CENode Server Admin Interface</h1>';
|
||||||
|
s += '<div style="width:48%;float:left;"><h2>Conceptual model</h2>';
|
||||||
|
s += '<p>Add CE sentences to the node:</p><form action="/ui/sentences" enctype="application/x-www-form-urlencoded" method="POST"><textarea name="sentence" style="width:95%;height:100px;"></textarea><br /><br /><input type="submit" /></form></div>';
|
||||||
|
s += `<div style="width:48%;float:left;"><h2>Node settings</h2><p>Update local agent name:</p><form method="POST" action="/agent-name"><input type="text" name="name" value="${node.agent.name}" /><input type="submit" /></form>`;
|
||||||
|
s += '<p>Other options:</p><button onclick="window.location=\'/reset\';">Empty model</button>';
|
||||||
|
s += '</div><div style="clear:both;"></div>';
|
||||||
|
s += '<div style="display:inline-block;width:45%;float:left;"><h2>Concepts</h2>';
|
||||||
|
for (const concept of con) {
|
||||||
|
s += concept.name;
|
||||||
|
if (concept.parents.length) {
|
||||||
|
s += ` (${concept.parents[0].name})`;
|
||||||
|
}
|
||||||
|
s += '<br>';
|
||||||
|
}
|
||||||
|
s += '</div><div style="display:inline-block;width:45%;float:right;"><h2>Instances</h2>';
|
||||||
|
for (const instance of ins) {
|
||||||
|
s += `${instance.name} (${instance.type.name})<br>`;
|
||||||
|
}
|
||||||
|
s += '</div><body></html>';
|
||||||
|
response.writeHead(200, { 'Content-Type': 'text/html' });
|
||||||
|
response.end(s);
|
||||||
|
} else if (request.url.indexOf(GET_CARDS_ENDPOINT) === 0) {
|
||||||
|
response.writeHead(200, { 'Content-Type': 'text/ce' });
|
||||||
|
getCards(node, request, response);
|
||||||
|
} else if (request.url === '/reset') {
|
||||||
|
node.resetAll();
|
||||||
|
response.writeHead(302, { Location: '/' });
|
||||||
|
response.end();
|
||||||
|
} else {
|
||||||
|
response.writeHead(404);
|
||||||
|
response.end('404: Resource not found for method GET.');
|
||||||
|
}
|
||||||
|
} else if (request.method === 'POST') {
|
||||||
|
if (request.url.indexOf(GET_CARDS_ENDPOINT) === 0) {
|
||||||
|
let body = '';
|
||||||
|
request.on('data', (chunk) => { body += chunk; });
|
||||||
|
request.on('end', () => {
|
||||||
|
const ignores = body.split(/\\n|\n/);
|
||||||
|
response.writeHead(200, { 'Content-Type': 'text/ce' });
|
||||||
|
getCards(node, request, response, ignores);
|
||||||
|
});
|
||||||
|
} else if (request.url === POST_SENTENCES_ENDPOINT) {
|
||||||
|
response.writeHead(200, { 'Content-Type': 'text/ce' });
|
||||||
|
postSentences(node, request, response);
|
||||||
|
} else if (request.url === '/ui/sentences') {
|
||||||
|
response.writeHead(302, { Location: '/' });
|
||||||
|
postSentences(node, request, response);
|
||||||
|
} else if (request.url === '/agent-name') {
|
||||||
let body = '';
|
let body = '';
|
||||||
request.on('data', (chunk) => { body += chunk; });
|
request.on('data', (chunk) => { body += chunk; });
|
||||||
request.on('end', () => {
|
request.on('end', () => {
|
||||||
body = decodeURIComponent(body.replace('name=', '').replace(/\+/g, ' '));
|
body = decodeURIComponent(body.replace('name=', '').replace(/\+/g, ' '));
|
||||||
this.node.agent.setName(body);
|
node.agent.setName(body);
|
||||||
response.writeHead(302, { Location: '/' });
|
response.writeHead(302, { Location: '/' });
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
this.server = http.createServer((request, response) => {
|
|
||||||
response.setHeader('Access-Control-Allow-Origin', '*');
|
|
||||||
if (request.method in this.handlers) {
|
|
||||||
const path = request.url.indexOf('?') > 1 ? request.url.slice(0, request.url.indexOf('?')) : request.url;
|
|
||||||
if (path in this.handlers[request.method]) {
|
|
||||||
try {
|
|
||||||
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 POST.');
|
||||||
}
|
}
|
||||||
} else if (request.method === 'OPTIONS') {
|
|
||||||
response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
|
|
||||||
response.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
|
|
||||||
response.writeHead(200);
|
|
||||||
response.end();
|
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(405);
|
response.writeHead(405);
|
||||||
response.end('405: Method not allowed on this server.');
|
response.end('405: Method not allowed on this server.');
|
||||||
}
|
}
|
||||||
});
|
}).listen(port || 5555);
|
||||||
this.server.listen(this.port);
|
|
||||||
this.server.on('error', () => { this.node = undefined; });
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
if (this.server) {
|
|
||||||
delete this.node;
|
|
||||||
this.server.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const name = process.argv.length > 2 ? process.argv[2] : 'Moira';
|
startServer();
|
||||||
const port = process.argv.length > 3 ? process.argv[3] : 5555;
|
|
||||||
const models = process.argv.slice(4);
|
|
||||||
new CEServer(name, port, models).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = CEServer;
|
|
||||||
|
@ -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()) {
|
||||||
|
@ -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}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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) {
|
||||||
@ -212,12 +175,11 @@ class PolicyHandler {
|
|||||||
if (policy.start_time) {
|
if (policy.start_time) {
|
||||||
const startTime = policy.start_time;
|
const startTime = policy.start_time;
|
||||||
for (const card of cards) {
|
for (const card of cards) {
|
||||||
if (card.timestamp && card.is_froms.length) {
|
|
||||||
let toAgent = false;
|
let toAgent = false;
|
||||||
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;
|
||||||
@ -242,13 +204,12 @@ class PolicyHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handle(policy) {
|
handle(policy) {
|
||||||
if (policy.enabled === 'true' && policy.type.name in this.handlers) {
|
if (policy.enabled === 'true') {
|
||||||
this.handlers[policy.type.name](policy);
|
this.handlers[policy.type.name](policy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
@ -342,20 +324,21 @@ class QuestionParser {
|
|||||||
ins = this.node.getInstances(con, true);
|
ins = this.node.getInstances(con, true);
|
||||||
s = `All instances of type '${con}':`;
|
s = `All instances of type '${con}':`;
|
||||||
} else if (t.toLowerCase() === 'list instances') {
|
} else if (t.toLowerCase() === 'list instances') {
|
||||||
ins = this.node.getInstances();
|
ins = this.node.instances;
|
||||||
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;
|
||||||
|
@ -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) {
|
||||||
|
@ -44,22 +44,6 @@ describe('CEParser', function() {
|
|||||||
expect(node.concepts.plant.relationships.length).to.be(1);
|
expect(node.concepts.plant.relationships.length).to.be(1);
|
||||||
expect(node.concepts.plant.relationships[0].label).to.be('grows into');
|
expect(node.concepts.plant.relationships[0].label).to.be('grows into');
|
||||||
});
|
});
|
||||||
it('prevent multi-conceptualisation', () => {
|
|
||||||
node.addCE('conceptualise a ~ river ~ R');
|
|
||||||
node.addCE('conceptualise a ~ river ~ R');
|
|
||||||
let counter = 0;
|
|
||||||
for (const concept of node.concepts) {
|
|
||||||
if (concept.name === 'river'){
|
|
||||||
counter += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect(counter).to.equal(1);
|
|
||||||
});
|
|
||||||
it('ensure concepts can be addressed by synonyms', () => {
|
|
||||||
node.addCE('conceptualise a ~ seat ~ that ~ is expressed by ~ chair and has the value V as ~ height ~');
|
|
||||||
node.addCE('there is a chair named chair1 that has 43cm as height');
|
|
||||||
expect(node.instances.chair1.height).to.equal('43cm');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -106,50 +90,6 @@ describe('CEParser', function() {
|
|||||||
expect(node.instances.jane.subConcepts[0].name).to.be('barrister');
|
expect(node.instances.jane.subConcepts[0].name).to.be('barrister');
|
||||||
expect(node.instances.jane.subConcepts[1].name).to.be('londoner');
|
expect(node.instances.jane.subConcepts[1].name).to.be('londoner');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevent multi-instantiation', () => {
|
|
||||||
node.addCE('there is a person named Francesca');
|
|
||||||
node.addCE('there is a person named Francesca');
|
|
||||||
let counter = 0;
|
|
||||||
for (const instance of node.instances) {
|
|
||||||
if (instance.name === 'Francesca'){
|
|
||||||
counter += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect(counter).to.equal(1);
|
|
||||||
});
|
|
||||||
it('ensure instance CE is correct', () => {
|
|
||||||
node.addCE('there is an entity named Hagrid');
|
|
||||||
const hagrid = node.instances.hagrid;
|
|
||||||
expect(hagrid.ce).to.equal('there is a entity named \'Hagrid\'.');
|
|
||||||
node.addCE('the entity Hagrid is a person');
|
|
||||||
expect(hagrid.ce).to.equal('there is a entity named \'Hagrid\' that is a person.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ensure instances can be addressed by synonyms', () => {
|
|
||||||
node.addCE('conceptualise an ~ engineer ~ E');
|
|
||||||
node.addCE('there is a person named William that is expressed by Will');
|
|
||||||
node.addCE('the person Will is an engineer');
|
|
||||||
expect(node.instances.william.subConcepts[0].name).to.be('engineer');
|
|
||||||
});
|
|
||||||
it('ensure instances inherit properties from subConcepts', () => {
|
|
||||||
node.addCE('conceptualise a ~ borough ~ B');
|
|
||||||
node.addCE('conceptualise the londoner L ~ lives in ~ the borough B');
|
|
||||||
node.addCE('conceptualise the barrister B has the value V as ~ speciality ~');
|
|
||||||
node.addCE('there is a person named Amy that is a londoner and is a barrister');
|
|
||||||
node.addCE('the person Amy lives in the borough Chelsea and has \'family law\' as speciality');
|
|
||||||
expect(node.instances.amy.lives_in.name).to.be('Chelsea');
|
|
||||||
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() {
|
||||||
@ -178,12 +118,11 @@ describe('CEParser', function() {
|
|||||||
expect(node.instances.fred.works_for.name).to.be('IBM');
|
expect(node.instances.fred.works_for.name).to.be('IBM');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('the person Fred works for the company IBM and is married to the person Jane and has 53 as age and has the city Cardiff as address.', function() {
|
it('the person Fred works for the company IBM and is married to the person Jane and has 53 as age.', function() {
|
||||||
node.addCE('conceptualise a ~ company ~ C');
|
node.addCE('conceptualise a ~ company ~ C');
|
||||||
node.addCE('conceptualise a ~ city ~ C');
|
node.addCE('conceptualise a ~ person ~ P that ~ works for ~ the company C and ~ is married to ~ the person Q and has the value V as ~ age ~');
|
||||||
node.addCE('conceptualise a ~ person ~ P that ~ works for ~ the company C and ~ is married to ~ the person Q and has the value V as ~ age ~ and has the city C as ~ address ~');
|
|
||||||
node.addCE('there is a person named Fred');
|
node.addCE('there is a person named Fred');
|
||||||
node.addCE('the person Fred works for the company IBM and is married to the person Jane and has 53 as age and has the city Cardiff as address.');
|
node.addCE('the person Fred works for the company IBM and is married to the person Jane and has 53 as age.');
|
||||||
expect(node.instances.fred.works_for.name).to.be('IBM');
|
expect(node.instances.fred.works_for.name).to.be('IBM');
|
||||||
expect(node.instances.fred.is_married_to.name).to.be('Jane');
|
expect(node.instances.fred.is_married_to.name).to.be('Jane');
|
||||||
expect(node.instances.fred.age).to.be('53');
|
expect(node.instances.fred.age).to.be('53');
|
||||||
@ -219,5 +158,7 @@ describe('CEParser', function() {
|
|||||||
expect(node.concepts.farmer.parents[1].name).to.be('land owner');
|
expect(node.concepts.farmer.parents[1].name).to.be('land owner');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user