From f8bd82f33d38446b77183fd67d36f907d6c8e04c Mon Sep 17 00:00:00 2001 From: x0x7 Date: Fri, 26 Jul 2024 18:47:43 -0515 Subject: [PATCH] Added files --- data.js | 92 ++++++++++ data2.js | 93 ++++++++++ easystart.js | 9 + nice.js | 330 +++++++++++++++++++++++++++++++++++ package.json | 16 ++ server.js | 77 +++++++++ setupdb.js | 34 ++++ sqlitedb.js | 4 + test.data.js | 8 + test.js | 6 + todo.js | 475 +++++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 1144 insertions(+) create mode 100644 data.js create mode 100644 data2.js create mode 100755 easystart.js create mode 100644 nice.js create mode 100644 package.json create mode 100755 server.js create mode 100755 setupdb.js create mode 100644 sqlitedb.js create mode 100644 test.data.js create mode 100644 test.js create mode 100644 todo.js diff --git a/data.js b/data.js new file mode 100644 index 0000000..39fdb52 --- /dev/null +++ b/data.js @@ -0,0 +1,92 @@ +var exports = module.exports; +//var {Database} = require('sqlite3'); +//var sqldb = new Database(_data+'/data.db'); +var {Client} = require('cassandra-driver'); +const cass = new Client({ + cloud: {secureConnectBundle:'secure-connect-todo.zip'}, + credentials: { + username:'', + password:'' + }, +}); + +async function startcass() { + await cass.connect(); + cass.execute('use todo'); +} +startcass(); + +(()=>{ + var rankCache = new Map(); + var rankReadTimeouts = new Map(); + var rankWriteTimeouts = new Map(); + var rankReadBounce = 1000*60*5; + var rankWriteBounce = 1000*15; + function rawgetrank(user,cb) { + cass.execute('Select json from rank where user=?',[user]).then(result=>{ + if(!result || !result.rows || !result.rows.length) return cb(null,{}); + cb(null,JSON.parse(result.rows[0].json)); + }).catch(cb); + } + + function updaterankreadtimeout(user) { + if(rankReadTimeouts.has(user)) clearTimeout(rankReadTimeouts.get(user)); + rankReadTimeouts.set(user,setTimeout(()=>rankCache.delete(user),rankReadBounce)); + } + + function getrank(user,cb) { + updaterankreadtimeout(user); + if(rankCache.has(user)) return cb(null,rankCache.get(user)); + rawgetrank(user,(err,obj)=>{ + if(err) return cb(err); + rankCache.set(user,obj); + return cb(null,obj); + }); + } + + function rawwriterank(user,obj) { + cass.execute('Insert into rank (user,json) values (?,?)',[user,JSON.stringify(obj)]); + } + + function writerank(user,obj) { + if(rankWriteTimeouts.has(user)) clearTimeout(rankWriteTimeouts.get(user)); + rankWriteTimeouts.set(user,setTimeout(()=>rawwriterank(user,obj),rankWriteBounce)); + } + exports.getrank=getrank; + exports.writerank=writerank; +})(); + +(()=>{ + function rawgettitle(user,id,cb) { + cass.execute('Select title from titles where user=? and id=?',[user,id]).then(result=>{ + if(!result || !result.rows || !result.rows.length) return cb('title not found'); + cb(null,result.rows[0]); + }).catch(cb); + } + function rawwritetitle(user,id,title) { + cass.execute('Insert into titles (user,id,title) values (?,?,?)',[user.id,title]); + } + exports.gettitle=rawgettitle; + exports.writetitle=rawwritetitle; +})(); + +(()=>{ + function rawgetday(user,day,cb) { + cass.execute('Select json from day where user=? and day=?',[user,day]).then(result=>{ + if(!result || !result.rows || !result.rows.length) return cb(null,[]); + cb(null,JSON.parse(result.rows[0].json)); + }).catch(cb); + } + function rawwriteday(user,day,list) { + cass.execute('Insert into day (user,day,json) values (?,?,?)',[user,day,JSON.stringify(list)]); + } + exports.getday=rawgetday; + exports.writeday=rawwriteday; +})(); + +(()=>{ + function rawaddlog(user,title,id) { + cass.execute('Insert into addjournal (user,time,title,id) values (?,?,?,?)',[user,Date.now(),title,id]); + } + exports.addlogwrite=rawaddlog; +})(); diff --git a/data2.js b/data2.js new file mode 100644 index 0000000..24eb2ad --- /dev/null +++ b/data2.js @@ -0,0 +1,93 @@ + +var readwritecache2 = require('../readwritecache2'); +var fs=require('fs'); +var _data = __dirname+'/data'; +var sqlitedb = require('./sqlitedb'); +var sqldb = 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(); + +module.exports.ranks=readwritecache2({ + default:{}, + writefreq:1000*30, + readfreq:1000*60*5, + delayclearonread:true, + delaywriteonwrite:true, + get:(key,cb)=>{ + console.log('Reading ranks for',key); + sqlitedb.get("SELECT json FROM rank WHERE user=?",key,(err,json)=>{ + if(err) return cb(err); + if(!json) return cb(); + try { + cb(null,JSON.parse(json.json)); + } + catch(e) { + cb(e); + } + }); + }, + write:(key,obj,notindb,cb)=>{ + //console.log('Writing ranks for',key); + if(notindb) return sqlitedb.run('INSERT INTO rank (user,json) VALUES (?,?)',key,JSON.stringify(obj),cb); + sqlitedb.run('UPDATE rank SET json=? WHERE user=?',JSON.stringify(obj),key,cb); + } +}); + +module.exports.journals=readwritecache2({ + default:{}, + get:(key,cb)=>{ + var user = key.split('.')[0]; + var date=key.split('.day.')[1].replace('.json',''); + //var date=key.toString(); + sqldb.get('Select json from day where user=? and day=?',user,date,(err,result)=>{ + if(err) return cb(err); + if(result) return cb(null,JSON.parse(result.json)); + //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); + } + }); + //}); + }); + }, + write:(key,obj,notindb,cb)=>{ + var user = key.split('.')[0]; + var day = key.split('.day.')[1]; + var stringified=JSON.stringify(obj); + //cass.execute('Insert into day (user,day,json) values (?,?,?)',[user,day,stringified]); + fs.writeFile(_data+'/'+key+'.json',stringified,cb); + if(notindb) { + sqldb.run('INSERT into day (user,day,json) values (?,?,?)',user,day,stringified); + } + else { + sqldb.run('UPDATE day SET json=? WHERE user=? AND day=?',stringified,user,day); + } + }, + writefreq:1000*30, + readfreq:1000*60*5, + delayclearonread:true, + delaywriteonwrite:true +}); diff --git a/easystart.js b/easystart.js new file mode 100755 index 0000000..bff9736 --- /dev/null +++ b/easystart.js @@ -0,0 +1,9 @@ +#!/usr/bin/env node + +var repl = require('repl'); +var user = process.argv[2]||'me'; +var todo=require('./nice')(user); +var context=repl.start(user+'> ').context; + +context.todo=todo; +Object.keys(todo).forEach(k=>context[k]=todo[k]); diff --git a/nice.js b/nice.js new file mode 100644 index 0000000..48cb0d9 --- /dev/null +++ b/nice.js @@ -0,0 +1,330 @@ +module.exports=exports; +var todo=require('./todo'); +var {pullexposedtasks,randomtasks,edittitle,searchtasks,pullmirrorsuper,pullmirrortask,getjournal,decayranks,completetask,pulltasks,addtask,removetask,ranktasks,removetasks,addmany}=todo; +Object.keys(todo).forEach(k=>exports[k]=todo[k]); +var prompt=require('prompt-sync')(); +var fs=require('fs'); +var async=require('async'); + +function noop() {} + +function exports(user) { + var retr={}; + retr.exports=exports; + retr.random=random; + retr.rand=random; + function random(count=5) { + randomtasks(user,count,(err,res)=>{ + console.log(); + res.forEach(i=>console.log(i.id,i.title)); + }); + } + retr.add=add; + function add() { + if(arguments.length==0) { + var list=[]; + function ask() { + var line=prompt(':'); + if(line) { + list.push(line); + ask(); + } + else { //Add list + addmany(user,list,console.log); + } + //if(line) addtask(user,line,ask); + } + return ask(); + } + Array.prototype.forEach.bind(arguments)(item=>{ + if(typeof(item)==='string') addtask(user,item,noop); + else { //Asuming list + item.forEach(item=>addtask(user,item,noop)); + } + }); + } + retr.remove=remove; + function remove() { + Array.prototype.forEach.bind(arguments)(item=>{ + if(typeof(item)==='number') { + if(item<=4) { + var verify = prompt('Verify: Y/n'); + if(verify[0]=='n'||verify[0]=='N') return; + } + removetask(user,item,noop); + } + else { //Asuming list + item.forEach(item=>removetask(user,item,noop)); + } + }); + } + function printtasks(tasks,stream) { + pastpull=tasks; + try { + tasks.sort(()=>Math.random()-0.5); + stream.write('\n'); + //console.log({tasks}); + tasks.forEach((t,i)=>{ + //console.log(t.id+':',i,t.title); + stream.write(t.id+': '+i+' '+t.title+'\n'); + }); + } + catch(e) { + console.log({user,tasks}); + console.log(e); + } + } + retr.pullexposed = pullexposed; + function pullexposed(count=4,stream=process.stdout) { + pullexposedtasks(user,count,(err,tasks)=>{ + if(err) return console.log(err,tasks); + printtasks(tasks,stream); + }); + } + var pastpull=[]; + retr.pull=pull; + retr.p = pull; + function pull(count=4,stream=process.stdout) { + pulltasks(user,count,(err,tasks)=>{ + if(err) return console.log(err,tasks); + printtasks(tasks,stream); + }); + } + retr.r=r; + var pulllog=fs.createWriteStream(__dirname+'/data/logs/pull.log',{flags:'a'}); + //function r() { + // if(arguments.length===1 && typeof(arguments[0])==='string') { + // return retr.done(arguments[0]); + // } + // var expanded=[]; + // Array.prototype.forEach.bind(arguments)(item=>{ + // if(typeof(item)==='number') expanded.push(item); + // else { //Asuming list + // expanded=expanded.concat(item); + // } + // }); + // var toptitle = pastpull[expanded[0]].title; + // expanded=expanded.map(i=>pastpull[i].id); + // //console.log(expanded) + // ranktasks(user,expanded,noop); + // pulllog.write(toptitle+'\n'); + // pastrank=expanded; + //} + function r() { + if (arguments.length === 1 && typeof(arguments[0]) === 'string') { + return retr.done(arguments[0]); + } + var expanded = []; + Array.prototype.forEach.call(arguments, item => { + if (typeof(item) === 'number') { + expanded.push([item]); // Wrap single numbers in an array + } else if (Array.isArray(item)) { + expanded.push(item); // Use array as is + } + }); + var toptitle = pastpull[expanded[0][0]].title; // Use the first element of the first array to get the title + expanded = expanded.map(group => group.map(i => pastpull[i].id)); // Replace temp ids with longhand ids + ranktasks(user, expanded, noop); + pulllog.write(toptitle + '\n'); + pastrank = expanded.flat(); + } + retr.addfromfile=addfromfile; + function addfromfile(path='insert') { + fs.readFile(path,(err,buff)=>{ + if(err) return console.log(err); + addmany(user,buff.toString().split('\n').filter(i=>i.length),console.log); + }); + } + retr.done=done; + function done(taskid) { + if(taskid<=4) { + var verify = prompt('Verify: Y/n'); + if(verify[0]=='n'||verify[0]=='N') return; + } + completetask(user,taskid,err=>{ + if(err) return console.log(err); + decayranks(user,1-5/300,noop); + //if(typeof(taskid)=='number') setTimeout(pm,100); + }); + } + retr.d=d; + function d() { + done(pastrank[0]); + } + var pastrank=[]; + retr.rank=rank; + //function rank() { + // var expanded=[]; + // Array.prototype.forEach.bind(arguments)(item=>{ + // if(typeof(item)==='number') expanded.push(item); + // else { //Asuming list + // expanded=expanded.concat(item); + // } + // }); + // pastrank=expanded; + // ranktasks(user,expanded,noop); + //} + function rank() { + var expanded = []; + if (arguments.length === 1 && Array.isArray(arguments[0])) { + expanded = arguments[0]; // Treat single array argument as the complete taskGroups + } else { + // Treat each argument as an element to be passed to ranktasks + Array.prototype.forEach.call(arguments, item => { + if (typeof(item) === 'number') { + expanded.push([item]); // Wrap single numbers in an array + } else { + expanded.push(item); // Already an array, use as is + } + }); + } + pastrank = expanded.flat(); + ranktasks(user, expanded, noop); + } + retr.pullmirror=pullmirror; + retr.pm=pullmirror; + retr.pullmirrormonth=pullmirrormonth; + retr.pullmirrorweek=pullmirrorweek; + retr.pullmirrorday=pullmirrorday; + function pullmirror() { + //async.parallel([pullmirrormonth,pullmirrorweek,pullmirrorday],(err,res)=>{ + async.map([doublepullmirrormonth,pullmirrormonth,doublepullmirrorweek,pullmirrorweek,doublepullmirrorday,pullmirrorday],(func,cb)=>{ + func((err,res)=>{ + if(err == 'Journal missing - skiplimit reached') return cb(); + cb(err,res); + }); + },(err,res)=>{ + if(err) return console.log(err); + res=res.filter(r=>r); + res.forEach(item=>{ + if(!item) return console.log('missing'); + console.log((new Date(item.time)).toLocaleString(),item.title,item.id||''); + }); + }); + } + function pullmirrorday(cb) { + pullmirrortask(user,cb); + } + function doublepullmirrorday(cb) { + getjournal(user,0,(err,journal)=>{ + if(err) return console.log(err); + if(!journal.list) return cb(); + pullmirrorsuper(user,1,journal.list.length*2,5,cb); + }); + } + retr.lookback=lookback; + function lookback(days) { + getjournal(user,days,(err,journal)=>{ + if(err) return console.log(err); + if(!journal.list) return console.log('Journal missing'); + journal.list.forEach(item=>{ + console.log((new Date(item.time)).toLocaleString(),item.title,item.id||''); + }); + }); + } + retr.pullmirrorweek=pullmirrorweek; + function pullmirrorweek(cb) { + var day=(new Date()).getDay()||7; + var pending=day; + var lookcount=0; + for(var i=0;i{ + --pending; + if(journal && journal.list) lookcount+=journal.list.length; + if(!pending) { + pullmirrorsuper(user,day,lookcount,5,cb); + } + }); + } + } + retr.doublepullmirrorweek=doublepullmirrorweek; + function doublepullmirrorweek(cb) { + var day=(new Date()).getDay()||7; + var pending=day; + var lookcount=0; + for(var i=0;i{ + --pending; + if(journal && journal.list) lookcount+=journal.list.length; + if(!pending) { + pullmirrorsuper(user,day,lookcount*2,5,cb); + } + }); + } + } + retr.pullmirrormonth=pullmirrormonth; + function pullmirrormonth(cb) { + var day=(new Date()).getDate()||30; + var pending=day; + var lookcount=0; + for(var i=0;i{ + --pending; + if(journal && journal.list) lookcount+=journal.list.length; + if(!pending) { + pullmirrorsuper(user,day,lookcount,15,cb); + } + }); + } + } + retr.doublepullmirrormonth=doublepullmirrormonth; + function doublepullmirrormonth(cb) { + var day=(new Date()).getDate()||30; + var pending=day; + var lookcount=0; + for(var i=0;i{ + --pending; + if(journal && journal.list) lookcount+=journal.list.length; + if(!pending) { + pullmirrorsuper(user,day,lookcount*2,15,cb); + } + }); + } + } + function lookup(ref) { + if(!ref) return; + if(typeof(ref)=='number') return lookupid(ref); + else return lookupname(ref); + } + retr.search=search; + function search(titlefrag) { + var args=[]; + for(var i=0;arguments[i];++i) { + args=args.concat(arguments[i]); + } + searchtasks(user,args,console.log); + } + retr.searchres=searchres; + function searchres(titlefrag) { + var args=[]; + for(var i=0;arguments[i];++i) { + args=args.concat(arguments[i]); + } + return new Promise((resolve,reject)=>{ + searchtasks(user,args,(err,results)=>{ + if(err) return reject(err); + resolve(results); + }); + }); + } + retr.edit = edit; + function edit(id,title) { + edittitle(user,id,title); + } + /* + http.createServer((req,res)=>{ + if(req.url=='/pull') { + res.writeHead(200,{'Content-type':'text/plain'}); + pulltasks(user,5,(err,tasks)=>{ + tasks.forEach((t,i)=>{ + //console.log(t.id+':',i,t.title); + res.write(t.id+': '+i+' '+t.title+'\n'); + }); + res.end(); + }); + } + }).listen(7000); + */ + return retr; +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..683206f --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "todo2", + "version": "1.0.0", + "description": "", + "main": ".eslintrc.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "prompt-sync": "^4.2.0", + "sqlite3": "^5.1.4" + } +} diff --git a/server.js b/server.js new file mode 100755 index 0000000..4963f6e --- /dev/null +++ b/server.js @@ -0,0 +1,77 @@ +#!/usr/bin/env node +var async = require('async'); +var UrlResolver = require('urlresolver'); +var p = new UrlResolver(); +var user = 'me'; +exports.p = p; + +var todo=require('./todo'); +var {pulltasks,addtask,removetask,ranktasks,removetasks}=todo; +Object.keys(todo).forEach(k=>exports[k]=todo[k]); + +exports.de=de; +function de(res,err,code) { + res.writeHead(code||400,{"Content-Type":'text/plain'}); + res.end(err.toString()); +} + +exports.simpcb=simpcb; +function simpcb(res) { + return (err,data)=>{ + if(err) return de(res,err); + if(data) return res.end(data.toString()); + res.end('success'); + }; +} + + +p.add('/pull',(req,res)=>{ + pulltasks(user,5,(err,tasks)=>{ + if(err) return cb(err); + res.writeHead(200,{"Content-Type":'text/plain'}); + res.end(tasks.map((i,ind)=>`${i.id}: ${ind} ${i.title}`).join('\n')); + }); +}); + + +p.add('/api/pull',(req,res)=>{ + //Replace with req.user + pulltasks(user,5,(err,tasks)=>{ + if(err) return cb(err); + res.writeHead(200,{"Content-Type":'application/json'}); + res.end(JSON.stringify(tasks)); + }); +}); + + +p.add('/add/task/',(req,res)=>{ + addtask(req.user,req.post.string,simpcb(res)); +}); + + +p.add('/remove/task',(req,res)=>{ + var taskid=parseInt(req.post.string); + if(isNaN(taskid)) return de(res,"Not a number"); + removetask(req.user,taskid,simpcb(res)); +}); + + +p.add('/rank/tasks',(req,res)=>{ + var tasks=req.post.string.split(',').map(i=>parseInt(i)).filter(i=>!isNaN(i)); + ranktasks(req.user,tasks,simpcb(res)); +}); + + +p.add('/rank/and/remove/tasks',(req,res)=>{ + var [rank,remove]=req.post.string.split('|').map(list=>list.split(',').map(i=>parseInt(i)).filter(i=>!isNaN(i))); + async.parallel([ + cb=>ranktasks(req.user,rank,cb), + cb=>removetasks(req.user,remove,cb) + ],simpcb(res)); +}); + +function exports(req,res) { + p.get(req.url)(req,res); +} + +require('http').createServer(exports).listen(8092); diff --git a/setupdb.js b/setupdb.js new file mode 100755 index 0000000..32f9dc9 --- /dev/null +++ b/setupdb.js @@ -0,0 +1,34 @@ +#!/usr/bin/env node +/* + sqlitedb.get("SELECT json FROM rank WHERE user=?",key,(err,json)=>{ + if(notindb) return sqlitedb.run('INSERT INTO rank (user,json) VALUES (?,?)',key,JSON.stringify(obj),cb); + sqlitedb.run('UPDATE rank SET json=? WHERE user=?',JSON.stringify(obj),key,cb); +*/ + +var db = require('./sqlitedb'); + +db.run('CREATE TABLE rank (user text primary key,json text)',console.log); + +/* +todo.js: async.map(ids,(id,cb)=>sqldb.get('Select id,title from titles where user=? and id=?',user,id,cb),cb); +todo.js: sqldb.run('Insert into titles values (?,?,?)',user,i,tasktext,noop); +todo.js: sqldb.run('Insert into titles values (?,?,?)',user,i,tasktext,noop); +todo.js: sqldb.get('Select title from titles where user=? and id=?',user,taskid,(err,result)=>{ +todo.js: sqldb.run('Delete from titles where user=? and id=?',user,taskid,err=>{ +todo.js: sqldb.get('Select title from titles where user=? and id=?',user,taskid,(err,titleobj)=>{ +todo.js: sqldb.all(`Select id,title from titles where user=? and ${titlefrags}`,user,cb); +todo.js: sqldb.run('Update titles Set title=? Where user=? and id=?',title,user,id,cb); +todo.js: async.map(selectedids,(id,cb)=>sqldb.get('Select id,title from titles where user=? and id=?',user,id,cb),cb); +*/ + +db.run('CREATE TABLE titles (user text,id integer,title text)'); +db.run('CREATE INDEX titles_idx ON titles(user,id)'); + +/* +data2.js: sqldb.get('Select json from day where user=? and day=?',user,date,(err,result)=>{ +data2.js: sqldb.run('INSERT into day (user,day,json) values (?,?,?)',user,day,stringified); +data2.js: sqldb.run('UPDATE day SET json=? WHERE user=? AND day=?',stringified,user,day); +*/ + +CREATE TABLE day (user text not null,day text not null,json text not null); +CREATE UNIQUE INDEX day_idx ON day(user,day); diff --git a/sqlitedb.js b/sqlitedb.js new file mode 100644 index 0000000..4741741 --- /dev/null +++ b/sqlitedb.js @@ -0,0 +1,4 @@ + + +var sqlite3 = require('sqlite3'); +module.exports = new sqlite3.Database(__dirname+'/data/data.db'); diff --git a/test.data.js b/test.data.js new file mode 100644 index 0000000..9cc4e45 --- /dev/null +++ b/test.data.js @@ -0,0 +1,8 @@ + +function test1() { + var a = require('./data'); + setTimeout(()=>{ + a.getrank('test',console.log); + },1000); +} +test1(); diff --git a/test.js b/test.js new file mode 100644 index 0000000..103f068 --- /dev/null +++ b/test.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + + +var a = require('./server'); + +a.pulltasks('test',5,console.log); diff --git a/todo.js b/todo.js new file mode 100644 index 0000000..4456f8b --- /dev/null +++ b/todo.js @@ -0,0 +1,475 @@ +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)=>aremovetask(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{ + 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{ + if(err) return cb(err); + var i; + for(i=0;i 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{ + 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!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(allidsMath.random()-0.5).slice(0,5); + } + else { + selectedids=new Set(); + while(selectedids.sizesqldb.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.lengthundercount(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); +};