diff options
| author | Paul Buetow <paul@buetow.org> | 2023-02-12 13:31:51 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2023-02-12 13:31:51 +0200 |
| commit | 7529cdce08fa461e42c7932985bd9448f8722e5f (patch) | |
| tree | cf478f42b1f74fd9d59775b266de0d0e0e6f0c81 | |
| parent | e2b1981f930873f580d93cb57c278d6d9274055e (diff) | |
initial reporter
| -rw-r--r-- | src/guprecords.raku | 113 |
1 files changed, 66 insertions, 47 deletions
diff --git a/src/guprecords.raku b/src/guprecords.raku index 629a6e0..1d8e6dc 100644 --- a/src/guprecords.raku +++ b/src/guprecords.raku @@ -2,41 +2,46 @@ use v6.d; +#= The stats major category. +subset Cat of Str where * eq any <hostname os os-major uname>; +#= The sub-cateogory. +subset SubCat of Str where * eq any <boots uptime downtime lifespan>; + class Aggregate { - has Int $.total-uptime; + has Str $.name is required; + has Int $.uptime; has Int $.first-boot; has Int $.last-seen; - has Int $.elems; + has Int $.boots; - method aggregate(Str :$uptime is readonly, Str :$boot-time is readonly) { - $!total-uptime += $uptime; + method add-record(Str:D :$uptime is readonly, Str:D :$boot-time is readonly) { + $!uptime += $uptime; $!first-boot = +$boot-time if not defined $!first-boot or $!first-boot > $boot-time; my Int $last-seen = $uptime + $boot-time; $!last-seen = $last-seen if not defined $!last-seen or $!last-seen < $last-seen; - $!elems++; + $!boots++; } - method total-downtime { $.last-seen - $.first-boot - $.total-uptime } - method total-time { self.total-downtime + $.total-uptime } + method downtime { $.last-seen - $.first-boot - $.uptime } + method lifespan { self.downtime + $.uptime } method Str returns Str { - #duration($!total-uptime) - my Str $active = self.is-active ?? '* ' !! ' '; - return "$active {duration($!total-uptime)} {date($!last-seen)}"; + my Str $active = self!is-active ?? '* ' !! ' '; + return "$active {$!name}\t{duration($!uptime)} {$!uptime}" } - method is-active(Int \limit = 90) returns Bool { + method !is-active(Int:D \limit = 90) returns Bool { (DateTime.now - DateTime.new(Instant.from-posix: $!last-seen)) < limit * 3600 * 24; } - sub duration(Int \seconds) returns Str { + sub duration(Int:D \seconds) returns Str { my DateTime \dt .= new(Instant.from-posix: seconds); return "{dt.year-1970} years, {dt.month} months, {dt.day} days"; } - sub date(Int \epoch) returns Str { + sub date(Int:D \epoch) returns Str { DateTime.new(Instant.from-posix: epoch).yyyy-mm-dd } } @@ -44,48 +49,62 @@ class Aggregate { class Aggregator { has %.aggregates = { hostname => {}, os => {}, uname => {}, os-major => {} } - method aggregate(IO::Path :$file is readonly) { + method add-file(IO::Path:D :$file is readonly) { my Str $hostname = $file.IO.basename.split('.').first; - %!aggregates<hostname>{$hostname} //= Aggregate.new; - - for $file.IO.lines { - my Str ($uptime, $boot-time, $os) = .trim.split(':'); - my Str $uname = $os.split(' ').first; - my Str $os-major = "$uname {$os.split(' ')[1].split('.').first}..."; - - %!aggregates<os>{$os} //= Aggregate.new; - %!aggregates<uname>{$uname} //= Aggregate.new; - %!aggregates<os-major>{$os-major} //= Aggregate.new; - - for %!aggregates<hostname>{$hostname}, - %!aggregates<os>{$os}, - %!aggregates<uname>{$uname}, - %!aggregates<os-major>{$os-major} { - .aggregate(:$uptime, :$boot-time); - } + %!aggregates<hostname>{$hostname} //= Aggregate.new: :name($hostname); + for $file.IO.lines -> Str $line { self!add-line(:$line, :$hostname) } + } + + method !add-line(Str:D :$line is readonly, Str:D :$hostname is readonly) { + my Str ($uptime, $boot-time, $os) = $line.trim.split(':'); + my Str $uname = $os.split(' ').first; + my Str $os-major = "$uname {$os.split(' ')[1].split('.').first}..."; + + %!aggregates<os>{$os} //= Aggregate.new: :name($os); + %!aggregates<uname>{$uname} //= Aggregate.new: :name($uname); + %!aggregates<os-major>{$os-major} //= Aggregate.new: :name($os-major); + + for %!aggregates<hostname>{$hostname}, + %!aggregates<os>{$os}, + %!aggregates<uname>{$uname}, + %!aggregates<os-major>{$os-major} { + .add-record(:$uptime, :$boot-time); } } } -# TODO: -# --category switch (hostname, os, os-major, uname...) -# --stats switch (record count, total uptime, total downtime, total time/lifespan, -# individual uptime, individual downtime) +class Reporter { + has %.aggregates is required; + has Cat $.cat is required; + has SubCat $.sub-cat is required; + + method report { + for self.sort-by($!sub-cat) -> $what { + $what.Str.say; + } + } + + multi method sort-by('uptime') { self.sort-by: *.uptime } + multi method sort-by('downtime') { self.sort-by: *.downtime } + multi method sort-by('lifespan') { self.sort-by: *.lifespan } + multi method sort-by('boots') { self.sort-by: *.boots } + + multi method sort-by(Code:D $sort-by) { + %!aggregates{$!cat}.values.sort(&$sort-by).reverse + } +} + sub MAIN( - Str $in-dir = './stats', - Str $sort-by = 'uptime'; + Str :$stats-dir is required, #= The uptimed raw record input dir. + Cat :$cat = 'hostname'; #= Category, one of hostname, os os-major and uname. + SubCat :$sub-cat = 'uptime'; #= Sort by one of boots uptime downtime and lifespan. ) { - my Aggregator $aggregator .= new; + my Aggregator $agg .= new; - for dir($in-dir, test => { /\.records$/ }) -> $file { - $aggregator.aggregate(:$file) + for dir($stats-dir, test => { /\.records$/ }) -> $file { + $agg.add-file(:$file) } - for $aggregator.aggregates.kv -> $category, $aggregates { - say "Category $category"; - for $aggregates.kv -> $name, $agg { - my Str $plural = $agg.elems > 1 ?? 's' !! ''; - say "\t$name $agg (" ~ $agg.elems ~ " record$plural)"; - } - } + my Reporter $reporter .= new: :aggregates($agg.aggregates), :$cat, :$sub-cat; + $reporter.report; } |
