const BaseModel = require('./baseModel.js')
const ReasoningService = require('../services/reasoning.js')
const N3 = require('n3')
const { DataFactory } = N3
const { namedNode, literal, defaultGraph, quad } = DataFactory
// unlink temp files
const { unlink } = require('fs')
/**
* The stepModel contains code specifically for the step model
* Handles all steps related data
* @extends BaseModel
*/
class StepModel extends BaseModel {
constructor (req) {
super(req)
this.extendedFolder = 'steps'
this.fileName = 'data.ttl'
this.inputPatternFile = 'reasoning/interim/input/input_patterns.n3'
this.queryPatternFile = 'reasoning/interim/show/query-patterns.n3'
}
/**
* Gets the input patterns as n3string.
*
* @async
* @returns {string} n3string of all input patterns
*/
async getAllInputPatternsAsString () {
return this.fileService.fileToString(this.inputPatternFile)
}
/**
* Gets all query patterns as n3string
* @async
* @returns {string} n3string of all query patterns
*/
async getAllQueryPatternsAsString () {
return this.fileService.fileToString(this.queryPatternFile)
}
/**
* Get input pattern of the provided step
* @async
* @param {string} stepName - name of the step are interested in
* @returns {Object|null} - object with the following properties:
*
* - triples: n3string of the expected triples
* - replaceVar: n3string of the variables to replace
* - prefixes: all the prefixes for a step as a string
* - step: name of the step
*
* - returns 'null' if no match can be found for either the triples or if there are no variables to replace
*/
async getInputPatternByStep (stepName) {
// first get all input patterns as a string
const inputPatterns = await this.getAllInputPatternsAsString()
// get prefixes
const prefixes = this.fileService.prefixesFromStringAsString(inputPatterns)
// unused?:const prefixesJSON = this.fileService.prefixesFromString(inputPatterns);
// create the expressions that will find our pattern and variable to replace
const exprPattern = new RegExp(stepName + '.*{(.*)}', '')
/* eslint-disable-next-line */
const exprReplaceVar = new RegExp('.*' + stepName + '.*variablesToReplace.*\.', '')
// get the pattern triples using the RegExp
const matchedTriples = inputPatterns.match(exprPattern)
if (matchedTriples === null) {
return null
}
// Get captured group
const patternTriples = matchedTriples[1]
const matchedReplaceVar = inputPatterns.match(exprReplaceVar)
if (matchedReplaceVar === null) {
return null
}
const patternReplaceVar = matchedReplaceVar[0]
// var patternReplaceVar = inputPatterns.match(exprReplaceVar)[0];
// store everything as JSON object to return
const patternFull = {
triples: patternTriples,
replaceVar: patternReplaceVar,
prefixes: prefixes,
step: stepName
}
// console.log("getInputPatternByStep:", patternFull);
// beam me up Scotty
return patternFull
}
/**
* Transforms the provided steps into a temporary N3 file which gets fed to the reasoner.
*
* Which in turn returns the inferred N3 steps that we need to add to the users profile.
* SHOULD NEVER BE USED WITH SOLID (only possible to change internal data)
* @async
* @param {Object.<String,String|String[]>} steps - Stepname - stepvalue pairs.
* Stepvalue can be a string or an array of strings for steps that take multiple values
* @returns {String|null} - N3 strings inferred from the steps or null if no steps were provided.
*
*/
async getTriplesFromSteps (steps) {
if (Object.keys(steps).length === 0) {
console.log('No steps provided')
return null
}
const prefixes = {
step: this.prefixes.step,
// step : this.url + '/steps#',
' ': 'http://example.org#'
}
// loop the steps and create triples from the json request
// e.g. step:provideTelephoneNumber :value "123456"
const quads = []
for (const step in steps) {
// Always transform to array for simplicity
const values = Array.isArray(steps[step]) ? steps[step] : [steps[step]]
for (const val of values) {
quads.push(quad(
namedNode(prefixes.step + step),
namedNode(prefixes[' '] + 'value'),
literal(val),
defaultGraph()))
}
}
const tempFile = await this.fileService.quadsToTempFile(quads, prefixes)
// convert the quad to a string to be used by reasoner (or write to file first if reasoner can't use string)
const inference = {
query: 'reasoning/external-input/CreateInputTriple.n3',
data: [tempFile, 'reasoning/help-functions/aux2.n3', 'reasoning/profile/knowledge.n3', 'reasoning/profile/personalInfo.n3', 'reasoning/profile/profileInfo.n3', 'reasoning/interim/steps/component-level-steps.n3', 'reasoning/external-input/replaceValue.n3']
}
// Here comes the DAG invocation
const output = await ReasoningService.eyePromise(inference)
unlink(tempFile, (err) => {
if (err) { console.error('Temporary file: ', tempFile, ' Could not be removed', err) } else { console.log('Deleted temporary file: ', tempFile) }
})
return output
}
}
module.exports = StepModel
// Old code kept for tracking bugs
/*
* Turns a JSON request for adding a value to specific steps into triples the reasoner can use,
* the reasoner then generates all of the triples that are required for finishing the step
*/
/*
getStepTriplesByJSON(body, next)
{
return next(Boom.badRequest('Refactored into getTriplesFromSteps'), null);
// check if the request actually contains input, if not -> errorhandler
if (!body.hasOwnProperty('input'))
return next(Boom.badRequest('Invalid format for request'), null);
// if no steps were provided then return null
if (!Object.keys(body.input).length)
return next(null, null);
var prefixes = {
step : this.prefixes.step,
//step : this.url + '/steps#',
' ': 'http://example.org#'
};
// loop the steps and create triples from the json request
// e.g. step:provideTelephoneNumber :value "123456"
var quads = [];
for(var step in body.input){
const myQuad = quad(
namedNode(prefixes.step + step),
namedNode(prefixes[' '] + 'value'),
literal(body.input[step]),
defaultGraph(),
);
quads.push(myQuad);
}
// convert the quad to a string to be used by reasoner (or write to file first if reasoner can't use string)
this.fileService.quadsToTempFile(quads, prefixes, function(error, file) {
if (error) return next(error, null);
var query = 'reasoning/external-input/CreateInputTriple.n3';
var meta = {
inference: {
query: query,
data: [file, 'reasoning/help-functions/aux2.n3', 'reasoning/profile/knowledge.n3', 'reasoning/profile/personalInfo.n3', 'reasoning/profile/profileInfo.n3', 'reasoning/interim/steps/component-level-steps.n3', 'reasoning/external-input/replaceValue.n3' ]
}
}
//Here comes the DAG invocation
Promise.resolve(ReasoningService.eyePromise(meta.inference))
.then(function (body) {
// TODO: convert turtle result to json-ld
//console.log(body)
next(null, body);
//renderSupportedContentypes(context, targetContentType, body, res);
})
.catch(function (error) {
//renderError(res, error);
next(error, null);
});
});
}
*/