476 lines
13 KiB
JavaScript
476 lines
13 KiB
JavaScript
|
var exports=module.exports;
|
||
|
var _data = __dirname+'/data';
|
||
|
exports._data=_data;
|
||
|
|
||
|
var fs=require('fs');
|
||
|
var {shuffle} = require('lodash');
|
||
|
var async = require('async');
|
||
|
var df = require('../df');
|
||
|
var {ranks,journals} = require('./data2');
|
||
|
|
||
|
df((err,kb)=>{
|
||
|
if(err) return console.log('df err',err);
|
||
|
if(kb<1000) process.exit(1);
|
||
|
});
|
||
|
|
||
|
var db = require('../readwritecache')(dbwrite,dbwrite,dbread,{},1000,1);
|
||
|
//var {Database} = require('sqlite3');
|
||
|
var sqldb = require('./sqlitedb');
|
||
|
//var {Client} = require('cassandra-driver');
|
||
|
//const cass = new Client({
|
||
|
// cloud: {secureConnectBundle:'secure-connect-todo.zip'},
|
||
|
// credentials: {
|
||
|
// username:'jvonmitchell@gmail.com',
|
||
|
// password:'6e9hDX8$$'
|
||
|
// },
|
||
|
//});
|
||
|
|
||
|
//async function startcass() {
|
||
|
// await cass.connect();
|
||
|
// cass.execute('use todo');
|
||
|
//}
|
||
|
//startcass();
|
||
|
|
||
|
|
||
|
exports.db=db;
|
||
|
|
||
|
exports.dbwrite=dbwrite;
|
||
|
function dbwrite(key,obj,cb) {
|
||
|
//console.log("Writing",key);
|
||
|
var stringified=JSON.stringify(obj);
|
||
|
var user = key.split('.')[0];
|
||
|
if(key.endsWith('.rank')) {
|
||
|
sqldb.run('Update rank set json=? where user=?',stringified,user,noop);
|
||
|
cass.execute('Insert into rank (user,json) values (?,?)',[user,stringified]);
|
||
|
}
|
||
|
if(key.indexOf('.day.')!=-1) {
|
||
|
var day = key.split('.day.')[1];
|
||
|
cass.execute('Insert into day (user,day,json) values (?,?,?)',[user,day,stringified]);
|
||
|
}
|
||
|
fs.writeFile(_data+'/'+key+'.json',stringified,cb);
|
||
|
}
|
||
|
|
||
|
var rankCache = new Map();
|
||
|
var rankCacheTimeouts = new Map();
|
||
|
exports.dbread=dbread;
|
||
|
function dbread(key,cb) {
|
||
|
var user = key.split('.')[0];
|
||
|
if(key.endsWith('.rank')) {
|
||
|
if(rankCache.has(user)) {
|
||
|
clearTimeout(rankCacheTimeouts.get(user));
|
||
|
rankCacheTimeouts.set(user,setTimeout(()=>rankCache.delete(user)));
|
||
|
return cb(null,rankCache.get(user));
|
||
|
}
|
||
|
return sqldb.get('Select json from rank where user=?',user,(err,json)=>{
|
||
|
if(err) return cb(err);
|
||
|
//if(!json) return cb('User not found in rank');
|
||
|
if(!json) return cb();
|
||
|
json=JSON.parse(json.json);
|
||
|
rankCache.set(user,json);
|
||
|
rankCacheTimeouts.set(user,setTimeout(()=>rankCache.delete(user)));
|
||
|
cb(null,json);
|
||
|
});
|
||
|
}
|
||
|
if(key.indexOf('.day.')!=-1) {
|
||
|
//var indexOf = key.indexOf('.day.');
|
||
|
//console.log({key,indexOf});
|
||
|
var date=key.split('.day.')[1].replace('.json','');
|
||
|
return cass.execute('Select json from day where user=? and day=?',[user,date]).then(result=>{
|
||
|
if(result && result.rows && result.rows.length) {
|
||
|
//console.log({result});
|
||
|
return cb(null,JSON.parse(result.rows[0].json));
|
||
|
}
|
||
|
fs.readFile(_data+'/'+key+'.json',(err,buff)=>{
|
||
|
if(err) return cb();
|
||
|
try {
|
||
|
cb(null,JSON.parse(buff));
|
||
|
}
|
||
|
catch(e) {
|
||
|
console.log({key,buff:buff.toString(),err:e,cb});
|
||
|
cb(e);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
fs.readFile(_data+'/'+key+'.json',(err,buff)=>{
|
||
|
if(err) return cb();
|
||
|
try {
|
||
|
cb(null,JSON.parse(buff));
|
||
|
}
|
||
|
catch(e) {
|
||
|
console.log({key,buff:buff.toString(),err:e,cb});
|
||
|
cb(e);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
|
||
|
exports.minindexes=minindexes;
|
||
|
function minindexes(list) {
|
||
|
if(!list.length) return [];
|
||
|
var minvalue=list.reduce((a,b)=>a<b?a:b);
|
||
|
var retr=[];
|
||
|
for(var i=0;i<list.length;++i) {
|
||
|
if(list[i]==minvalue) retr.push(i);
|
||
|
}
|
||
|
//console.log({minvalue,idexes:retr});
|
||
|
return retr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
exports.removetasks=removetasks;
|
||
|
function removetasks(user,tasks,cb) {
|
||
|
async.map(tasks,(task,cb)=>removetask(user,cb),cb);
|
||
|
}
|
||
|
|
||
|
function noop () {}
|
||
|
|
||
|
exports.addtask=addtask;
|
||
|
function addtask(user,tasktext,cb) {
|
||
|
//var rankkey=user+'.rank';
|
||
|
var addhistkey=user+'.addhist';
|
||
|
//async.map([rankkey,addhistkey],db.get,(err,objs)=>{
|
||
|
async.parallel([
|
||
|
cb=>ranks.get(user,cb),
|
||
|
cb=>db.get(addhistkey,cb)
|
||
|
],(err,objs)=>{
|
||
|
if(err) return cb(err);
|
||
|
var [rankobj,addhistobj]=objs;
|
||
|
var i=0;
|
||
|
while(rankobj[i]) ++i;
|
||
|
sqldb.run('Insert into titles values (?,?,?)',user,i,tasktext,noop);
|
||
|
rankobj[i]={};
|
||
|
if(!addhistobj.hist) addhistobj.hist=[];
|
||
|
addhistobj.hist.push(tasktext);
|
||
|
//console.log({rankkey,rankobj,i,tasktext});
|
||
|
//db.markWrite(rankkey);
|
||
|
ranks.markWrite(user);
|
||
|
db.markWrite(addhistkey);
|
||
|
cb(null,i);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
exports.addmany=addmany;
|
||
|
function addmany(user,tasktexts,cb) {
|
||
|
//var rankkey=user+'.rank';
|
||
|
//db.get(rankkey,(err,rankobj)=>{
|
||
|
ranks.get(user,(err,rankobj)=>{
|
||
|
if(err) return cb(err);
|
||
|
var retr=[];
|
||
|
var i=0;
|
||
|
tasktexts.forEach(tasktext=>{
|
||
|
if(!tasktext) return;
|
||
|
while(rankobj[i]) ++i;
|
||
|
retr.push(i);
|
||
|
//sqldb.run('BEGIN TRANSACTION',noop);
|
||
|
sqldb.run('Insert into titles values (?,?,?)',user,i,tasktext,noop);
|
||
|
//sqldb.run('COMMIT',noop);
|
||
|
rankobj[i]={};
|
||
|
});
|
||
|
//db.markWrite(rankkey);
|
||
|
ranks.markWrite(user);
|
||
|
cb(null,retr);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
exports.removetask=removetask;
|
||
|
function removetask(user,taskid,cb) {
|
||
|
//var rankkey=user+'.rank';
|
||
|
//db.get(rankkey,(err,rankobj)=>{
|
||
|
ranks.get(user,(err,rankobj)=>{
|
||
|
if(err) return cb(err);
|
||
|
sqldb.get('Select title from titles where user=? and id=?',user,taskid,(err,result)=>{
|
||
|
if(err) return cb(err);
|
||
|
if(result) console.log('Removed:',result.title);
|
||
|
sqldb.run('Delete from titles where user=? and id=?',user,taskid,err=>{
|
||
|
if(err) return cb(err);
|
||
|
console.log('removing',taskid);
|
||
|
delete rankobj[taskid];
|
||
|
Object.values(rankobj).forEach(peerobj=>{
|
||
|
delete peerobj[taskid];
|
||
|
});
|
||
|
//db.markWrite(rankkey);
|
||
|
ranks.markWrite(user);
|
||
|
cb();
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
exports.completetask=completetask;
|
||
|
function completetask(user,taskid,cb) {
|
||
|
var now=Date.now();
|
||
|
var journalkey=user+'.day.'+(new Date()).toLocaleDateString().replace(/\//g,'.');
|
||
|
//db.get(journalkey,(err,journalobj)=>{
|
||
|
journals.get(journalkey,(err,journalobj)=>{
|
||
|
if(err) return cb(err);
|
||
|
var title;
|
||
|
journalobj.list=journalobj.list||[];
|
||
|
if(typeof(taskid)==='string') {
|
||
|
title=taskid;
|
||
|
journalobj.list.push({time:now,title});
|
||
|
journals.markWrite(journalkey);
|
||
|
//db.markWrite(journalkey);
|
||
|
}
|
||
|
else {
|
||
|
sqldb.get('Select title from titles where user=? and id=?',user,taskid,(err,titleobj)=>{
|
||
|
if(err) return cb(err);
|
||
|
title=titleobj.title;
|
||
|
journalobj.list.push({time:now,title,id:taskid});
|
||
|
removetask(user,taskid,cb);
|
||
|
journals.markWrite(journalkey);
|
||
|
//db.markWrite(journalkey);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
exports.undercount=undercount;
|
||
|
function undercount(id,obj,blockset) {
|
||
|
if(blockset.has(id)) return 1000;
|
||
|
var count=0;
|
||
|
var keys=Object.keys(obj);
|
||
|
if(!keys.length) return 0;
|
||
|
for(var i=0;i<keys.length;++i) {
|
||
|
if(blockset.has(keys[i])) continue;
|
||
|
if(obj[keys[i]]<0) count-=obj[keys[i]];
|
||
|
}
|
||
|
//console.log({id,count});
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
exports.decayranks=decayranks;
|
||
|
const DECAY=5/300;
|
||
|
function decayranks(user,decay,cb) {
|
||
|
//var key=user+'.rank';
|
||
|
decay=decay||DECAY;
|
||
|
//db.get(key,(err,obj)=>{
|
||
|
ranks.get(user,(err,obj)=>{
|
||
|
if(err) return cb(err);
|
||
|
Object.values(obj).forEach(subobj=>{
|
||
|
Object.keys(subobj).forEach(subkey=>{
|
||
|
if(subkey=='block') return;
|
||
|
subobj[subkey]*=decay;
|
||
|
});
|
||
|
});
|
||
|
//db.markWrite(key);
|
||
|
ranks.markWrite(user);
|
||
|
cb();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
exports.getjournal=getjournal;
|
||
|
function getjournal(user,days,cb) {
|
||
|
days=days||0;
|
||
|
days=Math.abs(days);
|
||
|
var journalkey=user+'.day.'+(new Date(Date.now()-1000*60*60*24*days)).toLocaleDateString().replace(/\//g,'.');
|
||
|
//db.get(journalkey,cb);
|
||
|
journals.get(journalkey,cb);
|
||
|
}
|
||
|
|
||
|
//exports.ranktasks=ranktasks;
|
||
|
//function ranktasks(user,tasklist,cb) {
|
||
|
// //var key=user+'.rank';
|
||
|
// //db.get(key,(err,obj)=>{
|
||
|
// ranks.get(user,(err,obj)=>{
|
||
|
// if(err) return cb(err);
|
||
|
// var i;
|
||
|
// for(i=0;i<tasklist.length;++i) {
|
||
|
// for(var j=i+1;j<tasklist.length;++j) {
|
||
|
// obj[tasklist[i]][tasklist[j]]=1;
|
||
|
// obj[tasklist[j]][tasklist[i]]=-1;
|
||
|
// }
|
||
|
// }
|
||
|
// for(i=1;i<tasklist.length;++i) {
|
||
|
// obj[tasklist[i]].block=i; //We don't want to see it for a bit, add a block that will count down
|
||
|
// //console.log("Block",tasklist[i],i);
|
||
|
// }
|
||
|
// //db.markWrite(key);
|
||
|
// ranks.markWrite(user);
|
||
|
// cb();
|
||
|
// });
|
||
|
//}
|
||
|
|
||
|
exports.ranktasks=ranktasks;
|
||
|
function ranktasks(user,taskGroups,cb) {
|
||
|
ranks.get(user,(err,obj)=>{
|
||
|
if(err) return cb(err);
|
||
|
var i;
|
||
|
for(i=0;i<taskGroups.length;++i) {
|
||
|
for(var j=i+1;j<taskGroups.length;++j) {
|
||
|
for(var taskA of [].concat(taskGroups[i])) {
|
||
|
for(var taskB of [].concat(taskGroups[j])) {
|
||
|
obj[taskA][taskB]=1;
|
||
|
obj[taskB][taskA]=-1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (let i = 1; i < taskGroups.length; ++i) {
|
||
|
[].concat(taskGroups[i]).forEach(task => obj[task].block = i);
|
||
|
}
|
||
|
ranks.markWrite(user);
|
||
|
cb();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
exports.searchtasks=searchtasks;
|
||
|
function searchtasks(user,titlefrags,cb) {
|
||
|
//This is not safe
|
||
|
if(typeof(titlefrags)=='string') titlefrags = [titlefrags];
|
||
|
titlefrags = titlefrags.map(i=>i.toLowerCase());
|
||
|
titlefrags = titlefrags.map(i=>`title like '%${i}%'`);
|
||
|
titlefrags = titlefrags.join(' and ');
|
||
|
sqldb.all(`Select id,title from titles where user=? and ${titlefrags}`,user,cb);
|
||
|
}
|
||
|
|
||
|
exports.edittitle=edittitle;
|
||
|
function edittitle(user,id,title,cb) {
|
||
|
sqldb.run('Update titles Set title=? Where user=? and id=?',title,user,id,cb);
|
||
|
}
|
||
|
|
||
|
exports.pullmirrorsuper=pullmirrorsuper;
|
||
|
function pullmirrorsuper(user,daysback,donecount,skiplimit,cb) {
|
||
|
getjournal(user,daysback,(err,backjournal)=>{
|
||
|
if(err) return cb(err);
|
||
|
if(!backjournal.list) {
|
||
|
if(skiplimit) pullmirrorsuper(user,daysback+1,donecount,skiplimit-1,cb);
|
||
|
else return cb("Journal missing - skiplimit reached");
|
||
|
}
|
||
|
else if(backjournal.list.length<donecount) pullmirrorsuper(user,daysback+1,donecount-=backjournal.list.length,skiplimit,cb);
|
||
|
else cb(null,backjournal.list[backjournal.list.length-donecount]);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
|
||
|
exports.pullmirrortask=pullmirrortask;
|
||
|
function pullmirrortask(user,cb) {
|
||
|
var skiplimit=5;
|
||
|
getjournal(user,0,(err,journal0)=>{
|
||
|
if(err) return cb(err);
|
||
|
if(!journal0.list) return cb("Journal missing");
|
||
|
var donecount=journal0.list.length;
|
||
|
var daysback=1;
|
||
|
function getone() {
|
||
|
getjournal(user,daysback,(err,backjournal)=>{
|
||
|
if(err) return cb(err);
|
||
|
if(!backjournal.list) {
|
||
|
if(skiplimit) {
|
||
|
--skiplimit;
|
||
|
++daysback;
|
||
|
//console.log('One journal missing');
|
||
|
return getone();
|
||
|
}
|
||
|
return cb("Journal missing");
|
||
|
}
|
||
|
if(backjournal.list.length<donecount) {
|
||
|
donecount-=backjournal.list.length;
|
||
|
++daysback;
|
||
|
return getone();
|
||
|
}
|
||
|
cb(null,backjournal.list[backjournal.list.length-donecount]);
|
||
|
});
|
||
|
}
|
||
|
getone(1);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function filter(list,filterSet,maxcount) {
|
||
|
maxcount = maxcount || Infinity;
|
||
|
return list.filter(i=>!filterSet.has(i)).slice(0,maxcount);
|
||
|
}
|
||
|
|
||
|
exports.randomtasks=randomtasks;
|
||
|
function randomtasks(user,count,cb) {
|
||
|
ranks.get(user,(err,rank)=>{
|
||
|
var allids = Object.keys(rank);
|
||
|
var selectedids;
|
||
|
if(allids<count*count) {
|
||
|
selectedids=allids.sort(()=>Math.random()-0.5).slice(0,5);
|
||
|
}
|
||
|
else {
|
||
|
selectedids=new Set();
|
||
|
while(selectedids.size<count) {
|
||
|
selectedids.add(allids[Math.floor(allids.length*Math.random())]);
|
||
|
}
|
||
|
selectedids=Array.from(selectedids);
|
||
|
}
|
||
|
async.map(selectedids,(id,cb)=>sqldb.get('Select id,title from titles where user=? and id=?',user,id,cb),cb);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getRanksButOnlyExposed(user,cb) {
|
||
|
ranks.get(user,(err,rank)=>{
|
||
|
if(err) return cb(err);
|
||
|
var out={};
|
||
|
var ids=Object.keys(rank);
|
||
|
var validids = ids.filter(id=>Object.keys(rank[id]).length>0);
|
||
|
validids.forEach(id=>out[id]=rank[id]);
|
||
|
cb(null,out);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
|
||
|
exports.pulltaskids=pulltaskids;
|
||
|
function pulltaskids(user,count,cb,getter=ranks.get) {
|
||
|
//var key=user+'.rank';
|
||
|
var pulledids=[];
|
||
|
var pulledidsSet=new Set();
|
||
|
var filterSet=new Set();
|
||
|
//db.get(key,(err,rank)=>{
|
||
|
getter(user,(err,rank)=>{
|
||
|
if(err) return cb(err);
|
||
|
var rankkeys=Object.keys(rank);
|
||
|
var topcohort;
|
||
|
//console.log({rank,rankkeys});
|
||
|
//For blocking items with temp block.
|
||
|
rankkeys.forEach(i=>{ //If it currently has a block don't display it
|
||
|
//console.log('Block level',i,rank[i].block);
|
||
|
if(rank[i].block>0) {
|
||
|
//console.log('Blocking',i,rank[i].block);
|
||
|
rank[i].block=Math.floor(rank[i].block-1);
|
||
|
//console.log('Blocking',i,rank[i].block);
|
||
|
if(rank[i].block<=0) {
|
||
|
delete rank[i].block;
|
||
|
//console.log('Delete rank',i);
|
||
|
}
|
||
|
filterSet.add(i);
|
||
|
}
|
||
|
});
|
||
|
//db.markWrite(key);
|
||
|
ranks.markWrite(user);
|
||
|
while(pulledids.length<count) {
|
||
|
topcohort=minindexes(rankkeys.map(id=>undercount(id,rank[id],pulledidsSet))).map(i=>rankkeys[i]);
|
||
|
//console.log({rankkeys,topcohort});
|
||
|
if(topcohort.length==0) return cb(null,pulledids); //No more items to add. Return.
|
||
|
topcohort.forEach(id=>pulledidsSet.add(id));
|
||
|
topcohort=filter(shuffle(topcohort),filterSet,count-pulledids.length);
|
||
|
pulledids=pulledids.concat(topcohort);
|
||
|
if(pulledids.length==count) return cb(null,pulledids);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
exports.pulltasks=pulltasks;
|
||
|
function pulltasks(user,count,cb) {
|
||
|
pulltaskids(user,count,(err,ids)=>{
|
||
|
if(err) return cb(err);
|
||
|
console.log({ids});
|
||
|
async.map(ids,(id,cb)=>sqldb.get('Select id,title from titles where user=? and id=?',user,id,cb),cb);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
module.exports.pullexposedtasks=pullexposedtasks;
|
||
|
function pullexposedtasks(user,count,cb) {
|
||
|
pulltaskids(user,count,(err,ids)=>{
|
||
|
if(err) return cb(err);
|
||
|
console.log({ids});
|
||
|
async.map(ids,(id,cb)=>sqldb.get('Select id,title from titles where user=? and id=?',user,id,cb),cb);
|
||
|
},getRanksButOnlyExposed);
|
||
|
};
|