Compare commits

...

25 Commits

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

View File

@ -43,9 +43,9 @@ class CEAgent {
getInstance() { getInstance() {
const instances = this.node.getInstances('agent'); const instances = this.node.getInstances('agent');
for (let i = 0; i < instances.length; i += 1) { for (const instance of instances) {
if (instances[i].name.toLowerCase() === name.toLowerCase()) { if (instance.name.toLowerCase() === name.toLowerCase()) {
return instances[i]; return instance;
} }
} }
return null; return null;

View File

@ -19,6 +19,14 @@
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();
@ -30,99 +38,101 @@ class CEConcept {
node.concepts.push(this); node.concepts.push(this);
this.node.conceptDict[this.id] = this; this.node.conceptDict[this.id] = this;
const concept = this; if (isNaN(name[0])) {
Object.defineProperty(node.concepts, name.toLowerCase().replace(/ /g, '_'), { const concept = this;
get() { Object.defineProperty(node.concepts, name.toLowerCase().replace(/ /g, '_'), {
return concept; get() {
}, return concept;
configurable: true, },
}); configurable: true,
});
}
} }
get instances() { get instances() {
const instances = []; const array = [];
for (let i = 0; i < this.node.instances.length; i += 1) { for (const instance of this.node.instances) {
if (this.node.instances[i].type.id === this.id) { if (instance.concept.id === this.id) {
instances.push(this.node.instances[i]); array.push(instance);
} }
} }
return instances; return array;
} }
get allInstances() { get allInstances() {
const allConcepts = this.descendants.concat(this); const allConcepts = this.descendants.concat(this);
const instances = []; const array = [];
for (let i = 0; i < this.node.instances.length; i += 1) { for (const instance of this.node.instances) {
for (let j = 0; j < allConcepts.length; j += 1) { for (const concept of allConcepts) {
if (this.node.instances[i].type.id === allConcepts[j].id) { if (instance.concept.id === concept.id) {
instances.push(this.node.instances[i]); array.push(instance);
} }
} }
} }
return instances; return array;
} }
get parents() { get parents() {
const p = []; const array = [];
for (let i = 0; i < this.parentIds.length; i += 1) { for (const id of this.parentIds) {
p.push(this.node.getConceptById(this.parentIds[i])); array.push(this.node.getConceptById(id));
} }
return p; return array;
} }
get ancestors() { get ancestors() {
const parents = []; const array = [];
const stack = []; const stack = [];
for (let i = 0; i < this.parentIds.length; i += 1) { for (const parent of this.parents) {
stack.push(this.parents[i]); stack.push(parent);
} }
while (stack.length > 0) { while (stack.length > 0) {
const current = stack.pop(); const current = stack.pop();
parents.push(current); array.push(current);
for (let i = 0; i < current.parents.length; i += 1) { for (const parent of current.parents) {
stack.push(current.parents[i]); stack.push(parent);
} }
} }
return parents; return array;
} }
get children() { get children() {
const children = []; const array = [];
for (let i = 0; i < this.node.concepts.length; i += 1) { for (const concept of this.node.concepts) {
for (let j = 0; j < this.node.concepts[i].parents.length; j += 1) { for (const parent of concept.parents) {
if (this.node.concepts[i].parents[j].id === this.id) { if (parent.id === this.id) {
children.push(this.node.concepts[i]); array.push(concept);
} }
} }
} }
return children; return array;
} }
get descendants() { get descendants() {
const children = []; const array = [];
const stack = []; const stack = [];
for (let i = 0; i < this.children.length; i += 1) { for (const child of this.children) {
stack.push(this.children[i]); stack.push(child);
} }
while (stack.length > 0) { while (stack.length > 0) {
const current = stack.pop(); const current = stack.pop();
children.push(current); array.push(current);
const currentChildren = current.children; const currentChildren = current.children;
if (currentChildren) { if (currentChildren) {
for (let i = 0; i < currentChildren.length; i += 1) { for (const child of currentChildren) {
stack.push(currentChildren[i]); stack.push(child);
} }
} }
} }
return children; return array;
} }
get relationships() { get relationships() {
const rels = []; const rels = [];
for (let i = 0; i < this.relationshipIds.length; i += 1) { for (const id of this.relationshipIds) {
const relationship = {}; const relationship = {};
relationship.label = this.relationshipIds[i].label; relationship.label = id.label;
relationship.concept = this.node.getConceptById(this.relationshipIds[i].target); relationship.concept = this.node.getConceptById(id.target);
rels.push(relationship); rels.push(relationship);
} }
return rels; return rels;
@ -130,60 +140,61 @@ class CEConcept {
get values() { get values() {
const vals = []; const vals = [];
for (let i = 0; i < this.valueIds.length; i += 1) { for (const val of this.valueIds) {
const value = {}; const value = {};
value.label = this.valueIds[i].label; value.label = val.label;
if (this.valueIds[i].conceptId === 0) { value.concept = val.type && this.node.getConceptById(val.type);
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;
} }
get ce() { getCE(isModification) {
let ce = `conceptualise a ~ ${this.name} ~ ${this.name.charAt(0).toUpperCase()}`; let ce = '';
if (this.parentIds.length > 0 || this.valueIds.length > 0 || this.relationshipIds.length > 0) { if (isModification) {
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.parentIds.length; i += 1) { for (let i = 0; i < this.parents.length; i += 1) {
ce += ` is a ${this.parentIds[i].name}`; ce += ` is a ${this.parents[i].name}`;
if (i < this.parentIds.length - 1) { ce += ' and'; } if (i < this.parents.length - 1) { ce += ' and'; }
} }
} }
let facts = []; const 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}`; }
@ -219,12 +230,14 @@ 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);
Object.defineProperty(this, label.toLowerCase().replace(/ /g, '_'), { if (isNaN(label[0])) {
get() { Object.defineProperty(this, label.toLowerCase().replace(/ /g, '_'), {
return type === 0 ? 'value' : type; get() {
}, return type === 0 ? 'value' : type;
configurable: true, },
}); configurable: true,
});
}
} }
addRelationship(label, target, source) { addRelationship(label, target, source) {
@ -233,12 +246,14 @@ class CEConcept {
relationship.label = label; relationship.label = label;
relationship.target = target.id; relationship.target = target.id;
this.relationshipIds.push(relationship); this.relationshipIds.push(relationship);
Object.defineProperty(this, label.toLowerCase().replace(/ /g, '_'), { if (isNaN(label[0])) {
get() { Object.defineProperty(this, label.toLowerCase().replace(/ /g, '_'), {
return target; get() {
}, return target;
configurable: true, },
}); configurable: true,
});
}
} }
addParent(parentConcept) { addParent(parentConcept) {
@ -248,8 +263,8 @@ class CEConcept {
} }
addSynonym(synonym) { addSynonym(synonym) {
for (let i = 0; i < this.synonyms.length; i += 1) { for (const currentSynonym of this.synonyms) {
if (this.synonyms[i].toLowerCase() === synonym.toLowerCase()) { if (currentSynonym.toLowerCase() === synonym.toLowerCase()) {
return; return;
} }
} }

View File

@ -19,10 +19,14 @@
class CEInstance { class CEInstance {
constructor(node, type, name, source) { constructor(node, type, name, source) {
if (!type) { if (!type || !name) {
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;
@ -38,26 +42,28 @@ class CEInstance {
node.instances.push(this); node.instances.push(this);
this.node.instanceDict[this.id] = this; this.node.instanceDict[this.id] = this;
const instance = this; if (isNaN(name[0])) {
Object.defineProperty(node.instances, name.toLowerCase().replace(/ /g, '_').replace(/'/g, ''), { const instance = this;
get() { const helperName = name.toLowerCase().replace(/ /g, '_').replace(/'/g, '');
return instance; Object.defineProperty(node.instances, helperName, {
}, get() {
configurable: true, return instance;
}); },
configurable: true,
Object.defineProperty(type, name.toLowerCase(), { });
get() { Object.defineProperty(type, helperName, {
return instance; get() {
}, return instance;
configurable: true, },
}); configurable: true,
});
}
} }
get type() { get type() {
for (let i = 0; i < this.node.concepts.length; i += 1) { for (const concept of this.node.concepts) {
if (this.node.concepts[i].id === this.concept.id) { if (concept.id === this.concept.id) {
return this.node.concepts[i]; return concept;
} }
} }
return null; return null;
@ -65,11 +71,11 @@ class CEInstance {
get relationships() { get relationships() {
const rels = []; const rels = [];
for (let i = 0; i < this.relationshipIds.length; i += 1) { for (const id of this.relationshipIds) {
const relationship = {}; const relationship = {};
relationship.label = this.relationshipIds[i].label; relationship.label = id.label;
relationship.source = this.relationshipIds[i].source; relationship.source = id.source;
relationship.instance = this.node.getInstanceById(this.relationshipIds[i].targetId); relationship.instance = this.node.getInstanceById(id.targetId);
rels.push(relationship); rels.push(relationship);
} }
return rels; return rels;
@ -77,14 +83,14 @@ class CEInstance {
get values() { get values() {
const vals = []; const vals = [];
for (let i = 0; i < this.valueIds.length; i += 1) { for (const id of this.valueIds) {
const value = {}; const value = {};
value.label = this.valueIds[i].label; value.label = id.label;
value.source = this.valueIds[i].source; value.source = id.source;
if (this.valueIds[i].conceptId === 0) { if (id.conceptId === 0) {
value.instance = this.valueIds[i].typeName; value.instance = id.typeName;
} else { } else {
value.instance = this.node.getInstanceById(this.valueIds[i].conceptId); value.instance = this.node.getInstanceById(id.conceptId);
} }
vals.push(value); vals.push(value);
} }
@ -96,15 +102,19 @@ class CEInstance {
} }
getPossibleProperties() { getPossibleProperties() {
const ancestorInstances = this.concept.ancestors; let 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 (let i = 0; i < ancestorInstances.length; i += 1) { for (const ancestor of ancestorInstances) {
for (let j = 0; j < ancestorInstances[i].values.length; j += 1) { for (const value of ancestor.values) {
properties.values.push(ancestorInstances[i].values[j].label.toLowerCase()); properties.values.push(value.label.toLowerCase());
} }
for (let j = 0; j < ancestorInstances[i].relationships.length; j += 1) { for (const relationship of ancestor.relationships) {
properties.relationships.push(ancestorInstances[i].relationships[j].label.toLowerCase()); properties.relationships.push(relationship.label.toLowerCase());
} }
} }
return properties; return properties;
@ -123,7 +133,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) { if (this.reservedFields.indexOf(valueNameField) === -1 && isNaN(valueNameField[0])) {
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);
@ -135,9 +145,9 @@ class CEInstance {
Object.defineProperty(this, `${valueNameField}s`, { Object.defineProperty(this, `${valueNameField}s`, {
get() { get() {
const instances = []; const instances = [];
for (let i = 0; i < this.valueIds.length; i += 1) { for (const id of this.valueIds) {
if (this.valueIds[i].label.toLowerCase().replace(/ /g, '_') === valueNameField) { if (id.label.toLowerCase().replace(/ /g, '_') === valueNameField) {
instances.push(this.valueIds[i].conceptId === 0 ? this.valueIds[i].typeName : this.node.getInstanceById(this.valueIds[i].conceptId)); instances.push(id.conceptId === 0 ? id.typeName : this.node.getInstanceById(id.conceptId));
} }
} }
return instances; return instances;
@ -162,7 +172,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) { if (this.reservedFields.indexOf(relNameField) === -1 && isNaN(relNameField[0])) {
Object.defineProperty(this, relNameField, { Object.defineProperty(this, relNameField, {
get() { get() {
return this.node.getInstanceById(relationship.targetId); return this.node.getInstanceById(relationship.targetId);
@ -174,9 +184,9 @@ class CEInstance {
Object.defineProperty(this, `${relNameField}s`, { Object.defineProperty(this, `${relNameField}s`, {
get() { get() {
const instances = []; const instances = [];
for (let i = 0; i < this.relationshipIds.length; i += 1) { for (const id of this.relationshipIds) {
if (this.relationshipIds[i].label.toLowerCase().replace(/ /g, '_') === relNameField) { if (id.label.toLowerCase().replace(/ /g, '_') === relNameField) {
instances.push(this.node.getInstanceById(this.relationshipIds[i].targetId)); instances.push(this.node.getInstanceById(id.targetId));
} }
} }
return instances; return instances;
@ -195,17 +205,19 @@ class CEInstance {
if (!synonym || !synonym.length) { if (!synonym || !synonym.length) {
return null; return null;
} }
for (let i = 0; i < this.synonyms.length; i += 1) { for (const checkSynonym of this.synonyms) {
if (this.synonyms[i].toLowerCase() === synonym.toLowerCase()) { if (checkSynonym.toLowerCase() === synonym.toLowerCase()) {
return null; return null;
} }
} }
this.synonyms.push(synonym); this.synonyms.push(synonym);
Object.defineProperty(this, synonym.toLowerCase().replace(/ /g, '_'), { if (isNaN(synonym[0])) {
get() { Object.defineProperty(this, synonym.toLowerCase().replace(/ /g, '_'), {
return this; get() {
}, return this;
}); },
});
}
return null; return null;
} }
@ -250,69 +262,79 @@ class CEInstance {
return onlyOne ? null : properties; return onlyOne ? null : properties;
} }
get ce() { getCE(isModification) {
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 (let i = 0; i < this.valueIds.length; i += 1) { for (const id of this.valueIds) {
const value = this.valueIds[i]; if (id.conceptId === 0) {
if (value.conceptId === 0) { facts.push(`has '${id.typeName.replace(/'/g, "\\'")}' as ${id.label}`);
facts.push(`has '${value.typeName.replace(/'/g, "\\'")}' as ${value.label}`);
} else { } else {
const valueInstance = this.node.getInstanceById(value.conceptId); const valueInstance = this.node.getInstanceById(id.conceptId);
const valueConcept = valueInstance.type; const valueConcept = valueInstance.type;
facts.push(`has the ${valueConcept.name} '${valueInstance.name}' as ${value.label}`); facts.push(`has the ${valueConcept.name} '${valueInstance.name}' as ${id.label}`);
} }
} }
for (let i = 0; i < this.relationshipIds.length; i += 1) { for (const id of this.relationshipIds) {
const relationship = this.relationshipIds[i]; const relationshipInstance = this.node.getInstanceById(id.targetId);
const relationshipInstance = this.node.getInstanceById(relationship.targetId);
const relationshipConcept = relationshipInstance.type; const relationshipConcept = relationshipInstance.type;
facts.push(`${relationship.label} the ${relationshipConcept.name} '${relationshipInstance.name}'`); facts.push(`${id.label} the ${relationshipConcept.name} '${relationshipInstance.name}'`);
} }
if (facts.length > 0) { ce += ` that ${facts.join(' and ')}`; } if (facts.length > 0) { ce += `${!isModification && ' 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 (let i = 0; i < this.subConcepts.length; i += 1) { for (const subConcept of this.subConcepts) {
gist += ` and a ${this.subConcepts[i].name}`; gist += ` and a ${subConcept.name}`;
} }
gist += '.'; gist += '.';
const facts = {}; const facts = {};
let factFound = false; let factFound = false;
for (let i = 0; i < this.valueIds.length; i += 1) { for (const id of this.valueIds) {
factFound = true; factFound = true;
const value = this.valueIds[i];
let fact = ''; let fact = '';
if (value.conceptId === 0) { if (id.conceptId === 0) {
fact = `has '${value.typeName.replace(/'/g, "\\'")}' as ${value.label}`; fact = `has '${id.typeName.replace(/'/g, "\\'")}' as ${id.label}`;
} else { } else {
const valueInstance = this.node.getInstanceById(value.conceptId); const valueInstance = this.node.getInstanceById(id.conceptId);
const valueConcept = valueInstance.type; const valueConcept = valueInstance.type;
fact = `has the ${valueConcept.name} '${valueInstance.name}' as ${value.label}`; fact = `has the ${valueConcept.name} '${valueInstance.name}' as ${id.label}`;
} }
if (!(fact in facts)) { if (!(fact in facts)) {
facts[fact] = 0; facts[fact] = 0;
} }
facts[fact] += 1; facts[fact] += 1;
} }
for (let i = 0; i < this.relationshipIds.length; i += 1) { for (const id of this.relationshipIds) {
factFound = true; factFound = true;
const relationship = this.relationshipIds[i]; const relationshipInstance = this.node.getInstanceById(id.targetId);
const relationshipInstance = this.node.getInstanceById(relationship.targetId);
const relationshipConcept = relationshipInstance.type; const relationshipConcept = relationshipInstance.type;
const fact = `${relationship.label} the ${relationshipConcept.name} '${relationshipInstance.name}'`; const fact = `${id.label} the ${relationshipConcept.name} '${relationshipInstance.name}'`;
if (!(fact in facts)) { if (!(fact in facts)) {
facts[fact] = 0; facts[fact] = 0;
} }

View File

@ -46,13 +46,13 @@ class CENode {
getConceptByName(name) { getConceptByName(name) {
if (!name) { return null; } if (!name) { return null; }
for (let i = 0; i < this.concepts.length; i += 1) { for (const concept of this.concepts) {
if (this.concepts[i].name.toLowerCase() === name.toLowerCase()) { if (concept.name.toLowerCase() === name.toLowerCase()) {
return this.concepts[i]; return concept;
} }
for (let j = 0; j < this.concepts[i].synonyms.length; j += 1) { for (const synonym of concept.synonyms) {
if (this.concepts[i].synonyms[j].toLowerCase() === name.toLowerCase()) { if (synonym.toLowerCase() === name.toLowerCase()) {
return this.concepts[i]; return concept;
} }
} }
} }
@ -63,15 +63,17 @@ class CENode {
return this.instanceDict[id]; return this.instanceDict[id];
} }
getInstanceByName(name) { getInstanceByName(name, concept) {
if (!name) { return null; } if (!name) { return null; }
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 ? concept.id === instance.concept.id : true)) {
return this.instances[i]; if (instance.name.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()) { for (const synonym of instance.synonyms) {
return this.instances[i]; if (synonym.toLowerCase() === name.toLowerCase()) {
return instance;
}
} }
} }
} }
@ -95,15 +97,17 @@ class CENode {
* Returns: [obj{instance}] * Returns: [obj{instance}]
*/ */
getInstances(conceptType, recurse) { getInstances(conceptType, recurse) {
let instanceList = []; const instanceList = [];
if (!conceptType) { if (!conceptType) {
instanceList = this.instances; for (const instance of 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 (let i = 0; i < this.instances.length; i += 1) { for (const instance of this.instances) {
if (this.instances[i].type.id === concept.id) { if (instance && instance.concept.id === concept.id) {
instanceList.push(this.instances[i]); instanceList.push(instance);
} }
} }
} }
@ -112,10 +116,10 @@ class CENode {
if (concept) { if (concept) {
const descendants = concept.descendants.concat(concept); const descendants = concept.descendants.concat(concept);
const childrenIds = []; const childrenIds = [];
for (let i = 0; i < descendants.length; i += 1) { childrenIds.push(descendants[i].id); } for (const descendant of descendants) { childrenIds.push(descendant.id); }
for (let i = 0; i < this.instances.length; i += 1) { for (const instance of this.instances) {
if (childrenIds.indexOf(this.instances[i].type.id) > -1) { if (instance && childrenIds.indexOf(instance.concept.id) > -1) {
instanceList.push(this.instances[i]); instanceList.push(instance);
} }
} }
} }
@ -151,8 +155,8 @@ class CENode {
*/ */
addSentences(sentences, source) { addSentences(sentences, source) {
const responses = []; const responses = [];
for (let i = 0; i < sentences.length; i += 1) { for (const sentence of sentences) {
responses.push(this.addSentence(sentences[i], source)); responses.push(this.addSentence(sentence, source));
} }
return responses; return responses;
} }
@ -210,8 +214,10 @@ class CENode {
*/ */
loadModel(sentences) { loadModel(sentences) {
const responses = []; const responses = [];
for (let i = 0; i < sentences.length; i += 1) { if (sentences && sentences.length) {
responses.push(this.addCE(sentences[i])); for (const sentence of sentences) {
responses.push(this.addCE(sentence));
}
} }
return responses; return responses;
} }
@ -250,8 +256,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 (let i = 0; i < models.length; i += 1) { for (const model of models) {
this.loadModel(models[i]); this.loadModel(model);
} }
} }
} }

View File

@ -45,7 +45,7 @@ class CEParser {
const t = input.replace(/\s+/g, ' ').replace(/\.+$/, '').trim(); // Whitespace -> single space const t = input.replace(/\s+/g, ' ').replace(/\.+$/, '').trim(); // Whitespace -> single space
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/i)) { } else if (t.match(/^conceptualise the ([a-zA-Z0-9 ]*) ([A-Z0-9]+) (?:has|is|~)/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);
@ -75,6 +75,9 @@ class CEParser {
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 [false, 'Unable to parse sentence'];
}
const conceptName = conceptInfo[1]; const conceptName = conceptInfo[1];
const conceptVar = conceptInfo[2]; const conceptVar = conceptInfo[2];
const concept = this.node.getConceptByName(conceptName); const concept = this.node.getConceptByName(conceptName);
@ -137,14 +140,14 @@ class CEParser {
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); const currentInstance = this.node.getInstanceByName(instanceName, concept);
const instance = new CEInstance(this.node, concept, instanceName, source);
if (!concept) { if (!concept) {
return [false, `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 [true, 'There is already an instance of this type with this name.', currentInstance]; return [false, 'There is already an instance of this type with this name.', currentInstance];
} }
const instance = new CEInstance(this.node, concept, instanceName, source);
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/, '');
@ -158,21 +161,23 @@ class CEParser {
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); const names = t.match(/^the ([a-zA-Z0-9 ]*) '([^'\\]*(?:\\.[^'\\]*)*)'/i);
if (names) { if (names) {
concept = this.node.getConceptByName(names[1]); concept = this.node.getConceptByName(names[1]);
instance = this.node.getInstanceByName(names[2].replace(/\\/g, '')); instanceName = names[2].replace(/\\/g, '');
instance = this.node.getInstanceByName(instanceName, concept);
} }
} }
if (!instance && t.match(/^the ([a-zA-Z0-9 ]*)/i)) { 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 (let i = 0; i < this.node.concepts.length; i += 1) { for (const conceptCheck of this.node.concepts) {
if (names[1].toLowerCase().indexOf(this.node.concepts[i].name.toLowerCase()) === 0) { if (names[1].toLowerCase().indexOf(conceptCheck.name.toLowerCase()) === 0) {
concept = this.node.concepts[i]; concept = conceptCheck;
const instanceName = nameTokens[concept.name.split(' ').length].toLowerCase(); instanceName = nameTokens[concept.name.split(' ').length];
instance = concept[instanceName]; instance = this.node.getInstanceByName(instanceName, concept);
break; break;
} }
} }
@ -181,11 +186,14 @@ class CEParser {
return [false, `Unknown concept/instance combination in: ${t}`]; return [false, `Unknown concept/instance combination in: ${t}`];
} }
instance.sentences.push(t); instance.sentences.push(t);
const tokens = t.split(' ');
const remainder = t.replace(`'${instance.name}'`, instance.name).replace(`the ${concept.name} ${instance.name}`.trim(), ''); tokens.splice(0, 1 + concept.name.split(' ').length + instanceName.split(' ').length);
const remainder = tokens.join(' ');
const facts = remainder.replace(/\band\b/g, '+').match(/(?:'(?:\\.|[^'])*'|[^+])+/g); const facts = remainder.replace(/\band\b/g, '+').match(/(?:'(?:\\.|[^'])*'|[^+])+/g);
for (const fact of facts) { if (facts) {
this.processInstanceFact(instance, fact, source); for (const fact of facts) {
this.processInstanceFact(instance, fact, source);
}
} }
return [true, t, instance]; return [true, t, instance];
} }
@ -199,7 +207,7 @@ 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); 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);
} }
@ -219,7 +227,7 @@ 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);
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);
} }

View File

@ -20,141 +20,264 @@ 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');
const POST_SENTENCES_ENDPOINT = '/sentences'; class CEServer {
const GET_CARDS_ENDPOINT = '/cards';
function postSentences(node, request, response) { constructor(name, port, models) {
let body = ''; this.port = port;
request.on('data', (chunk) => { body += chunk; }); this.node = new CENode();
request.on('end', () => { if (models) {
body = decodeURIComponent(body.replace('sentence=', '').replace(/\+/g, ' ')); for (const model of models) {
const sentences = body.split(/\\n|\n/); this.node.loadModel(CEModels[model]);
const responses = node.addSentences(sentences); }
response.write(responses.map(resp => resp.data).join('\n')); }
response.end(); this.node.attachAgent();
}); this.node.agent.setName(name);
} this.handlers = {
GET: {
function getCards(node, request, response, ignoresInput) { '/cards': (request, response) => {
const url = decodeURIComponent(request.url); const agentRegex = decodeURIComponent(request.url).match(/agent=(.*)/);
const agentRegex = url.match(/agent=(.*)/); const agentStr = agentRegex ? agentRegex[1] : null;
const ignores = ignoresInput || []; const agents = (agentStr && agentStr.toLowerCase().split(',')) || [];
let agentStr = null; let s = '';
let agents = []; for (const card of this.node.getInstances('card', true)) {
if (agentRegex) { agentStr = agentRegex[1]; } for (const to of card.is_tos) {
if (agentStr) { for (const agent of agents) {
agents = agentStr.toLowerCase().split(','); if (to.name.toLowerCase() === agent) {
} s += `${card.ce}\n`;
const cards = node.getInstances('card', true); break;
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.writeHead(200, { 'Content-Type': 'text/ce' });
} response.end(s);
} },
} '/concepts': (request, response) => {
response.write(s); const concepts = [];
response.end(); for (const concept of this.node.concepts) {
} concepts.push({
name: concept.name,
function startServer() { id: concept.id,
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>'; 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 id = idRegex ? idRegex[1] : null;
const instance = this.node.getInstanceById(id);
if (instance) {
const body = {
name: instance.name,
conceptName: instance.concept.name,
conceptId: instance.concept.id,
ce: instance.ce,
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('Concept not found');
},
'/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 {
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 = '';
request.on('data', (chunk) => { body += chunk; });
request.on('end', () => {
body = decodeURIComponent(body.replace('sentence=', '').replace(/\+/g, ' '));
const sentences = body.split(/\\n|\n/);
const responses = this.node.addSentences(sentences);
response.writeHead(200, { 'Content-Type': 'text/ce' });
response.end(responses.map(resp => resp.data).join('\n'));
});
},
},
PUT: {
'/reset': (request, response) => {
this.node.resetAll();
response.writeHead(204);
response.end();
},
'/agent/name': (request, response) => {
let body = '';
request.on('data', (chunk) => { body += chunk; });
request.on('end', () => {
body = decodeURIComponent(body.replace('name=', '').replace(/\+/g, ' '));
this.node.agent.setName(body);
response.writeHead(302, { Location: '/' });
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]) {
this.handlers[request.method][path](request, response);
} else {
response.writeHead(404);
response.end(`404: Resource not found for method ${request.method}.`);
} }
s += '</div><div style="display:inline-block;width:45%;float:right;"><h2>Instances</h2>'; } else if (request.method === 'OPTIONS') {
for (const instance of ins) { response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
s += `${instance.name} (${instance.type.name})<br>`; response.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
} response.writeHead(200);
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(); response.end();
} else { } else {
response.writeHead(404); response.writeHead(405);
response.end('404: Resource not found for method GET.'); response.end('405: Method not allowed on this server.');
} }
} else if (request.method === 'POST') { });
if (request.url.indexOf(GET_CARDS_ENDPOINT) === 0) { this.server.listen(this.port);
let body = ''; this.server.on('error', () => { this.node = undefined; });
request.on('data', (chunk) => { body += chunk; }); }
request.on('end', () => {
const ignores = body.split(/\\n|\n/); stop() {
response.writeHead(200, { 'Content-Type': 'text/ce' }); if (this.server) {
getCards(node, request, response, ignores); delete this.node;
}); this.server.close();
} 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 = '';
request.on('data', (chunk) => { body += chunk; });
request.on('end', () => {
body = decodeURIComponent(body.replace('name=', '').replace(/\+/g, ' '));
node.agent.setName(body);
response.writeHead(302, { Location: '/' });
response.end();
});
} else {
response.writeHead(404);
response.end('404: Resource not found for method POST.');
}
} else {
response.writeHead(405);
response.end('405: Method not allowed on this server.');
} }
}).listen(port || 5555); }
} }
if (require.main === module) { if (require.main === module) {
startServer(); const name = process.argv.length > 2 ? process.argv[2] : 'Moira';
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;

View File

@ -175,29 +175,31 @@ 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) {
let toAgent = false; if (card.timestamp && card.is_froms.length) {
const tos = card.is_tos; let toAgent = false;
const from = card.is_froms[0]; const tos = card.is_tos;
const cardTimestamp = card.timestamp.name; const from = card.is_froms[0];
if (tos && parseInt(cardTimestamp, 10) > parseInt(startTime, 10)) { const cardTimestamp = card.timestamp.name;
for (const to of tos) { if (tos && parseInt(cardTimestamp, 10) > parseInt(startTime, 10)) {
if (to.name === this.agent.name) { // If card sent to THIS agent for (const to of tos) {
toAgent = true; if (to.name === this.agent.name) { // If card sent to THIS agent
break; toAgent = true;
} break;
}
if (toAgent) {
// Add each other agent as a recipient (if they aren't already)
for (const agentCheck of agents) {
let agentIsRecipient = false;
for (const to of tos) {
if (to.name.toLowerCase() === agentCheck.name.toLowerCase()) {
agentIsRecipient = true;
break;
}
} }
if (!agentIsRecipient && agentCheck.name.toLowerCase() !== this.agent.name.toLowerCase() && agentCheck.name.toLowerCase() !== from.name.toLowerCase()) { }
card.addRelationship('is to', agentCheck); if (toAgent) {
// Add each other agent as a recipient (if they aren't already)
for (const agentCheck of agents) {
let agentIsRecipient = false;
for (const to of tos) {
if (to.name.toLowerCase() === agentCheck.name.toLowerCase()) {
agentIsRecipient = true;
break;
}
}
if (!agentIsRecipient && agentCheck.name.toLowerCase() !== this.agent.name.toLowerCase() && agentCheck.name.toLowerCase() !== from.name.toLowerCase()) {
card.addRelationship('is to', agentCheck);
}
} }
} }
} }
@ -209,7 +211,7 @@ class PolicyHandler {
} }
handle(policy) { handle(policy) {
if (policy.enabled === 'true') { if (policy.enabled === 'true' && policy.type.name in this.handlers) {
this.handlers[policy.type.name](policy); this.handlers[policy.type.name](policy);
} }
} }

View File

@ -324,7 +324,7 @@ 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.instances; ins = this.node.getInstances();
s = 'All instances:'; s = 'All instances:';
} }
if (ins.length === 0) { if (ins.length === 0) {

View File

@ -44,6 +44,22 @@ 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');
});
}); });
@ -90,6 +106,41 @@ 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');
});
}); });
describe('Specific Examples', function() { describe('Specific Examples', function() {
@ -118,11 +169,12 @@ 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.', function() { 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() {
node.addCE('conceptualise a ~ company ~ C'); node.addCE('conceptualise a ~ company ~ 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 ~ 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 ~ 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.'); 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.');
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');
@ -158,7 +210,5 @@ 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');
}); });
}); });
}); });