|
|
@ -4,11 +4,14 @@ import readline from "readline"; |
|
|
|
import { program } from "commander"; |
|
|
|
import { program } from "commander"; |
|
|
|
import glob from "fast-glob"; |
|
|
|
import glob from "fast-glob"; |
|
|
|
import format from "date-fns/format/index.js"; |
|
|
|
import format from "date-fns/format/index.js"; |
|
|
|
|
|
|
|
import asciichart from "asciichart"; |
|
|
|
|
|
|
|
|
|
|
|
program |
|
|
|
program |
|
|
|
.option("--min <Number>", "The lowest count to print. Stop at this.", 1) |
|
|
|
.option("--min <Number>", "The lowest count to print. Stop at this.", 1) |
|
|
|
.option("--errors", "Show the errors so you can fix them.", false) |
|
|
|
.option("--errors", "Show the errors so you can fix them.", false) |
|
|
|
.option("--outfile <String>", "Save to file rather than stdout.") |
|
|
|
.option("--outfile <String>", "Save to file rather than stdout.") |
|
|
|
|
|
|
|
.option("--max-days <int>", "Only display charts for entries with at least this many days.", 10) |
|
|
|
|
|
|
|
.option("--height <int>", "Height of the ascii graph.", 5) |
|
|
|
.requiredOption("--input <String>", "Input file glob.") |
|
|
|
.requiredOption("--input <String>", "Input file glob.") |
|
|
|
.description("Loads the output of a chain parser and produces a report.") |
|
|
|
.description("Loads the output of a chain parser and produces a report.") |
|
|
|
.version(0.1); |
|
|
|
.version(0.1); |
|
|
@ -18,6 +21,10 @@ const OPTS = program.opts(); |
|
|
|
OPTS.min = parseInt(OPTS.min); |
|
|
|
OPTS.min = parseInt(OPTS.min); |
|
|
|
assert(!isNaN(OPTS.min), `min must be a number, you have ${OPTS.min}`); |
|
|
|
assert(!isNaN(OPTS.min), `min must be a number, you have ${OPTS.min}`); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OPTS.height = parseInt(OPTS.height); |
|
|
|
|
|
|
|
assert(!isNaN(OPTS.height), `height must be a number, you have ${OPTS.height}`); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const sort_request_chains = (chains, min) => { |
|
|
|
const sort_request_chains = (chains, min) => { |
|
|
|
const converted = []; |
|
|
|
const converted = []; |
|
|
|
|
|
|
|
|
|
|
@ -36,14 +43,24 @@ const sort_request_chains = (chains, min) => { |
|
|
|
|
|
|
|
|
|
|
|
const { stats, domain, result, generated_on} = JSON.parse(fs.readFileSync(OPTS.input)); |
|
|
|
const { stats, domain, result, generated_on} = JSON.parse(fs.readFileSync(OPTS.input)); |
|
|
|
|
|
|
|
|
|
|
|
const dates = Object.keys(result).sort(); |
|
|
|
const sorted = []; |
|
|
|
|
|
|
|
const keys = Object.keys(result).sort(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const by_chain = {}; |
|
|
|
|
|
|
|
|
|
|
|
for(let key of dates) { |
|
|
|
for(let key of keys) { |
|
|
|
console.log(`--- ${key} ---`); |
|
|
|
const chain = sort_request_chains(result[key], OPTS.min); |
|
|
|
|
|
|
|
|
|
|
|
const stats = sort_request_chains(result[key], OPTS.min); |
|
|
|
for(let [count, urls] of chain) { |
|
|
|
|
|
|
|
const series = by_chain[urls] || []; |
|
|
|
|
|
|
|
series.push(count); |
|
|
|
|
|
|
|
by_chain[urls] = series; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for(let [count, chain] of stats) { |
|
|
|
for(let [chain, counts] of Object.entries(by_chain)) { |
|
|
|
console.log(count, "\t", chain); |
|
|
|
if(counts.length > OPTS.maxDays) { |
|
|
|
|
|
|
|
console.log(chain); |
|
|
|
|
|
|
|
console.log(asciichart.plot(counts, {height: OPTS.height})); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|