Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rounding and Other Output Manipulation #35

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
96 changes: 76 additions & 20 deletions bin/cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,42 @@ var stringify = require('json-stable-stringify');
var parseTime = require('parse-messy-time');
var os = require('os');
var tmpdir = (os.tmpdir || os.tmpDir)();
var roundDate = require('round-date');

var argv = minimist(process.argv.slice(2), {
alias: { m: 'message', v: 'verbose', a: 'archive', t: 'type' }
});

var KEY_FORMAT = 'time!%F %T'

var HOME = process.env.HOME || process.env.USERPROFILE;
var datadir = argv.d || path.join(HOME, '.clocker');
var confile = argv.c || path.join(HOME, '.clocker', 'config.json');
mkdirp.sync(datadir);

var config = {
rnd: [
// 0: 'ceil' | 'floor' | 'round',
// 1: <n>,
// 2: 'seconds' | 'minutes' | 'hours'
],
fmt: { suppress_sec: false }
}

try {
var raw = fs.readFileSync(confile, 'utf8')
try {
config = JSON.parse(raw)
} catch(e) { error(e) }
} catch(e) {}

if (argv.rnd) config.rnd = argv.rnd.split(/[,:-\s]/g)
if (config.rnd && !Array.isArray(config.rnd)) config.rnd = config.rnd.split(/[,:-\s]/g)

var suppressSec = (argv.sec !== undefined ? !argv.sec :
(argv.sec === undefined && config && config.fmt && config.fmt.suppress_sec))


var db = level(path.join(datadir, 'db'), { valueEncoding: 'json' });

if (argv.h) usage(0)
Expand Down Expand Up @@ -92,10 +120,10 @@ else if (argv._[0] === 'status') {
});
var status = 'stopped';
s.once('data', function (row) {
var started = new Date(row.key.split('!')[1]);
var started = rndDate(new Date(row.key.split('!')[1]));
if (!row.value.end) {
var elapsed = (new Date) - started;
status = 'elapsed time: ' + fmt(elapsed);
var elapsed = rndDate(new Date) - started;
status = 'elapsed time: ' + fmt(elapsed, suppressSec);
}
});
s.once('end', function () {
Expand All @@ -121,11 +149,11 @@ else if (argv._[0] === 'data') {
};
s.pipe(through(write, function () {
var hours = rows.reduce(function reducer (acc, row) {
var start = new Date(row.key.split('!')[1]);
var end = row.value.end ? new Date(row.value.end) : new Date;
var start = rndDate(new Date(row.key.split('!')[1]));
var end = rndDate(row.value.end ? new Date(row.value.end) : new Date);
var key = strftime('%F', start);
if (key !== strftime('%F', end)) {
var nextDay = new Date(start);
var nextDay = rndDate(new Date(start));
nextDay.setDate(start.getDate() + 1);
nextDay.setHours(0);
nextDay.setMinutes(0);
Expand Down Expand Up @@ -176,9 +204,9 @@ else if (argv._[0] === 'csv') {
if (argv.type && !isRegExp(argv.type) && row.value.type !== argv.type) return;
if (argv.type && isRegExp(argv.type) && !testRegExp(argv.type, row.value.type)) return;

var start = new Date(row.key.split('!')[1]);
var end = row.value.end && new Date(row.value.end);
var elapsed = (end ? end : new Date) - start;
var start = rndDate(new Date(row.key.split('!')[1]));
var end = row.value.end && rndDate(new Date(row.value.end));
var elapsed = (end ? end : rndDate(new Date)) - start;

console.log('%s,%s,%s,%s,%s,%s,"%s","%s"',
toStamp(row.key),
Expand All @@ -205,17 +233,17 @@ else if (argv._[0] === 'list' || argv._[0] === 'ls') {
if (argv.type && isRegExp(argv.type) && !testRegExp(argv.type, row.value.type)) return;


var start = new Date(row.key.split('!')[1]);
var end = row.value.end && new Date(row.value.end);
var elapsed = (end ? end : new Date) - start;
var start = rndDate(new Date(row.key.split('!')[1]));
var end = row.value.end && rndDate(new Date(row.value.end));
var elapsed = (end ? end : rndDate(new Date)) - start;

console.log(
'%s %s [ %s - %s ] (%s)%s%s',
toStamp(row.key),
strftime('%F', start),
strftime('%T', start),
end ? strftime('%T', end) : 'NOW',
fmt(elapsed),
strftime(suppressSec ? '%R' : '%T', start),
end ? strftime(suppressSec ? '%R' : '%T', end) : 'NOW',
fmt(elapsed, suppressSec),
(row.value.type ? ' [' + row.value.type + ']' : ''),
(row.value.archive ? ' A' : '')
);
Expand Down Expand Up @@ -248,7 +276,7 @@ else if (argv._[0] === 'set') {
var value;

if (argv._.length < 3) {
return error('clocker set [STAMP] KEY VALUE');
error('clocker set [STAMP] KEY VALUE');
}
else if (argv._.length === 3) {
getLastRow(function (row) {
Expand Down Expand Up @@ -325,7 +353,7 @@ else if (argv._[0] === 'archive' || argv._[0] === 'unarchive') {
else usage(1)

function start (date, message, type, cb) {
var pkey = strftime('time!%F %T', d);
var pkey = strftime(KEY_FORMAT, d);
var tkey = 'time-type!' + type + '!' + strftime('%F %T', d);
db.batch([
{ type: 'put', key: pkey, value: { type: type, message: message } },
Expand Down Expand Up @@ -361,12 +389,12 @@ function pad (s, len) {
return Array(Math.max(0, len - String(s).length + 1)).join('0') + s;
}

function fmt (elapsed) {
function fmt (elapsed, suppressSec) {
var n = elapsed / 1000;
var hh = pad(Math.floor(n / 60 / 60), 2);
var mm = pad(Math.floor(n / 60 % 60), 2);
var ss = pad(Math.floor(n % 60), 2);
return [ hh, mm, ss ].join(':');
return (suppressSec ? [ hh, mm ] : [ hh, mm, ss ]).join(':');
}

function set (stamp, prop, value, originalValue) {
Expand Down Expand Up @@ -427,8 +455,10 @@ function toStamp (s) {
}

function getKey (x) {
if (x === 'NaN') return strftime(KEY_FORMAT, new Date(NaN));

if (!/^\d+$/.test(x)) return 'time!' + x;
return strftime('time!%F %T', new Date(x * 1000));
return strftime(KEY_FORMAT, new Date(x * 1000));
}

function getLastRow (callback) {
Expand Down Expand Up @@ -470,3 +500,29 @@ function isRegExp (str) {
function testRegExp (re, str) {
return RegExp(re.slice(1,-1)).test(str);
}

function rndDate(date) {
// off forced by flag --no-rnd or --rnd=''
if (argv.rnd === false, '||', (typeof argv.rnd === "string" && argv.rnd.length == 0)) return date;
// missing config
if (!config || !config.rnd || config.rnd.length == 0) return date;

var r = [].concat(config.rnd)
r[1] = parseFloat(r[1])
if (r.length !== 3
|| !/(?:round|ceil|floor)/.test(r[0])
|| isNaN(r[1])
|| !/(?:hours|minutes|seconds)/.test(r[2]))
error('Expected Rounding config like [{ceil, floor, round}, n, {hours, minutes, seconds}]; got: ' + JSON.stringify(r))

var meth = r[0]
var mult = {
hours: 60*60,
minutes: 60,
seconds: 1
}[r[2]]

if (!mult) error(JSON.stringify(r[2]) + " must be 'hours', 'minutes', or 'seconds'")

return roundDate[r[0]](r[1] * mult, date)
}
10 changes: 10 additions & 0 deletions bin/usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,13 @@ usage:
clocker unarchive {--lt=DATE, --gt=DATE}
clocker unarchive [STAMP...]
Un-archive a range of clocked records or a list of STAMPs.

Global Output Flags:
clocker (status|ls|data|csv) --rnd=<ceil|floor|round>:<n>:<hours|minutes|seconds>
Apply rounding function to stamps prior to output. e.g.
clocker ls --rnd=round:'30 seconds' (nearest half-minute)
clocker ls --rnd=ceil:'6 minutes' (tenths of hour, always up)
clocker ls --rnd=ceil-.1-hours (tenths of hour, always up)

clocker (status|ls) [--sec|--no-sec]
Don't display seconds
Loading