@graperank/interpreter
Version:
The GrapeRank Interpreter module generates normalized ratings from ingested network data. It requires one or more Protocols plugins.
182 lines (181 loc) • 9.75 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Interpreter = void 0;
const util_1 = require("@graperank/util");
class Interpreter {
protocols;
updateStatus;
stopping = false;
constructor(protocols, updateStatus) {
this.protocols = protocols;
this.updateStatus = updateStatus;
}
stop() {
this.stopping = true;
}
async interpret(raters, requests) {
// var protocol = new Protocols(this.factories)
var responses = [];
var ratings = [];
// var requests : ProtocolRequest[] = settings.interpreters
// `allraters` map keys hold all raters added as input and between protocol requests
// map value is the iteration number at which the rater was added
// (this number ends up in the scorecard as `dos` from observer)
const allraters = new Map();
var requestauthors;
if (!!raters && !!requests) {
console.log("GrapeRank : interpret : instantiating ", requests.length, " protocols for ", raters.length, " raters");
console.log("----------------------------------");
// add input raters to allraters
raters.forEach((userid) => allraters.set(userid, 0));
// loop through each interpreter request
// requests having `iterations` will ADD to `allraters` with each interation
// each request will use the `allraters` list from previous requests
for (let r in requests) {
if (this.stopping)
return undefined;
let requestindex = r;
let request = requests[requestindex];
this.protocols.setRequest(request);
// reset newraters, protocolratings, and newratings between protocol requests
const protocolratings = this.protocols.getInterpreted(request.protocol);
let newraters = new Set();
let newratings = new Map();
let thisiteration = 0;
let maxiterations = request.iterate || 1;
let thisiterationraters;
if (request.authors && request.authors.length)
requestauthors = new Set(request.authors);
let currentstatus;
console.log("GrapeRank : interpret : calling " + request.protocol + " protocol with params : ", this.protocols.get(request.protocol).params);
while (thisiteration < maxiterations) {
if (this.stopping)
return undefined;
// increment for each protocol iteration
thisiteration++;
thisiterationraters = requestauthors || (newraters?.size ? newraters : new Set(allraters.keys()));
console.log("GrapeRank : interpret : " + request.protocol + " protocol : begin iteration ", thisiteration, " of ", maxiterations, ", with ", thisiterationraters?.size, " raters");
// DEBUG
if (thisiterationraters.has(util_1.DEBUGTARGET))
console.log('DEBUGTARGET : interpret : target found in thisiteration raters');
try {
currentstatus = {
protocol: request.protocol,
// FIXME dos needs to be set on initial status ...
// how to determine this acurately BEFORE fetchData() has been called?
dos: request.iterate ? this.protocols.get(request.protocol)?.fetched?.length || 0 : undefined,
authors: thisiterationraters.size
};
if (this.updateStatus && !await this.updateStatus(currentstatus))
throw ('failed updating initial status');
let fetchstart = Date.now();
// fetch protocol specific dataset for requestauthors OR newraters OR allraters
let dos = await this.protocols.fetchData(request.protocol, thisiterationraters);
// TODO cache fetched data
currentstatus.fetched = [
this.protocols.get(request.protocol).fetched[dos - 1]?.size || 0, // number of fetched events
Date.now() - fetchstart, // duration of fetch request
thisiteration == maxiterations ? true : undefined // final DOS iteration ?
];
if (this.updateStatus && !await this.updateStatus(currentstatus))
throw ('failed updating status after fetch');
let interpretstart = Date.now();
// interpret fetched data and add to newratings
newratings = await this.protocols.interpret(request.protocol, dos);
currentstatus.interpreted = [
countRatingsMap(newratings) || 0, // number of interpretations rated
Date.now() - interpretstart, // duration of interpretation
thisiteration == maxiterations ? true : undefined // final DOS iteration ?
];
if (this.updateStatus && !await this.updateStatus(currentstatus))
throw ('failed updating status after interpret');
console.log("GrapeRank : interpret : ", request.protocol, " protocol : interpretation complete for iteration ", thisiteration);
// prepare for next iteration ONLY IF not on final iteration
if (thisiteration < maxiterations) {
// get new raters from interpreted newratings
newraters = getNewRaters(newratings, allraters);
// merge all raters to include new raters
newraters.forEach((rater) => allraters.set(rater, thisiteration));
console.log("GrapeRank : interpret : " + request.protocol + " protocol : added ", newraters.size, " new raters");
}
console.log("GrapeRank : interpretat : total ", allraters.size, " raters");
}
catch (e) {
console.log('GrapeRank : interpret : ERROR : ', e);
}
responses.push({
request: { ...request, params: this.protocols.get(request.protocol).params },
index: requestindex,
iteration: thisiteration,
numraters: thisiterationraters.size,
// TODO get numfetched from protocol
numfetched: undefined,
numratings: newratings.size
});
console.log("GrapeRank : interpret : " + request.protocol + " protocol : end iteration ", thisiteration, " of ", maxiterations);
console.log("----------------------------------");
}
// add the final map of protocolratings to ratings list
addToRatingsList(request.protocol, r, protocolratings, ratings);
}
// DEBUG duplicate ratings
let numtargetratings = new Map();
await (0, util_1.forEachBigArray)(ratings, (rating) => {
if (rating.ratee == util_1.DEBUGTARGET) {
let numratings = numtargetratings.get(rating.rater) || 0;
numtargetratings.set(rating.rater, numratings + 1);
}
});
numtargetratings.forEach((num, key) => {
if (num > 1)
console.log('DEBUGTARGET : interperet : found more than ONE rating for ', key);
});
}
else {
console.log('GrapeRank : ERROR in interpret() : no raterts && requests passed : ', raters, requests);
}
this.protocols.clear();
return { ratings, responses };
}
}
exports.Interpreter = Interpreter;
// FIXME this ONLY works when USERS are being rated, not CONTENT
// TODO extraction of new authors from rated content SHOULD be handled by each protocol ...
// TODO some protocols, like `nostr-mutes` && `nostr-reports`, should NOT append new ratees to allraters
// the scorecards generated should ONLY include "those ratees within the [`nostr-follows`] network" ...
// maybe there should be a designated protocol that "defines the set of new raters" ?
function getNewRaters(newratings, allraters) {
let newraters = new Set();
newratings.forEach((rateemap, rater) => {
rateemap.forEach((ratingdata, ratee) => {
if (!allraters || !allraters.has(ratee))
newraters.add(ratee);
});
});
// DEBUG
if (newraters.has(util_1.DEBUGTARGET))
console.log('DEBUGTARGET : interpret : target found by getNewRaters()');
return newraters;
}
function addToRatingsList(protocol, index, ratingsmap, ratingslist) {
ratingsmap.forEach((rateemap, rater) => {
rateemap.forEach((ratingdata, ratee) => {
ratingslist.push({
protocol,
index,
rater,
ratee,
...ratingdata
});
});
});
}
function countRatingsMap(ratingsmap) {
let count = 0;
ratingsmap.forEach((rateemap) => {
rateemap.forEach(() => {
count++;
});
});
return count;
}