use CSV::Parser; use Terminal::UI 'ui'; use Terminal::ANSI::OO 't'; # If no arguments are passed then run USAGE & exit. proto MAIN(|) is export {unless so @*ARGS {put $*USAGE; exit}; {*}} #| parses Call Logs multi sub MAIN ( Str $log where *.IO.f, #= input log file to parse UInt :$digits = 10, #= number of significant digits ) is export { my @logs; my Str %contacts{Str}; my Instant $timed = now; my Promise $initial = start { my $p = CSV::Parser.new(); # 0: Name, 1: Phone number, 2: Call Type, # 3: Timestamp, 4: Duration, 5: Sim used. # Turn the Hash to an Array. @logs = @($log.IO.lines.skip[^200].hyper.map({$p.parse($_)})>>.{0..4} # Discard invalid phone numbers. .grep(*.[1].chars >= $digits)); for @logs { # Discard non-significant digits from phone numbers. .[1] .= substr(*-10); # Removing leading/trailing whitespaces from names. .[0] .= trim; # Build contact list. %contacts{.[1]} = .[0] if .[0].chars > 0; # Store Duration in seconds. .[4] = (.[4].split(":")[0] * 60) + .[4].split(":")[1]; } }; ui.setup: :2panes; my $p0 = ui.panes[0]; my $p1 = ui.panes[1]; $p0.put: t.bold ~ t.bg-cyan ~ t.black ~t.underline~ "Taurus v" ~ $?DISTRIBUTION.meta; $p0.put: ""; with ui.screen.add-frame(:4height, :40width, :center) -> $f { with $f.add-pane -> $p { $f.draw; until $initial.status { $p.splash: "Parsing logs" ~ (". ", ".. ", "...")[$++ % 3]; sleep 0.8; } $p.splash: "Parsed {@logs.elems} entries in {now - $timed}s."; $p.put: " " x 18 ~ "Ok"; $p.select-last; ui.focus($f); } ui.get-key; ui.screen.remove-frame($f); ui.focus($f); } ui.refresh; $p0.put: "- Show Records", :meta(:all); $p0.put: ""; # First list Contacts, then sorted phone numbers. for @logs.race.map(*.[1]).unique.sort({%contacts{$_} // "Z", $_}) { $p0.put: "- " ~ $_ ~ " {%contacts{$_} // ''}", :meta(:number($_)); } $p1.clear; $p0.select-first; $p0.select(2); $p0.on: select => -> :%meta { if %meta { $p1.clear; with @logs { my Int $fmt = 16; my $outgoing = .grep(*.[2] eqv "Outgoing Call").map(*.[4]).sum; my $incoming = .grep(*.[2] eqv "Incoming Call").map(*.[4]).sum; $p1.put: sprintf("%-*s", $fmt, "Outgoing:") ~ $outgoing / 3600 ~ " hours" ; $p1.put: sprintf("%-*s", $fmt, "Incoming:") ~ $incoming / 3600 ~ " hours"; $p1.put: sprintf("%-*s", $fmt, "Total:") ~ ($incoming + $outgoing) / 3600 ~ " hours"; $p1.put: ""; } } elsif %meta -> $num { my Int $fmt = 18; $p1.clear; $p1.put: "Name: " ~ $_ with %contacts{$num}; $p1.put: "Number: " ~ $num; $p1.put: ""; with @logs.grep(*.[1] eqv $num).cache { my $outgoing = .grep(*.[2] eqv "Outgoing Call").map(*.[4]).sum; my $incoming = .grep(*.[2] eqv "Incoming Call").map(*.[4]).sum; $p1.put: sprintf("%-*s", $fmt, "Outgoing:") ~ $outgoing / 3600 ~ " hours"; $p1.put: sprintf("%-*s", $fmt, "Incoming:") ~ $incoming / 3600 ~ " hours"; $p1.put: sprintf("%-*s", $fmt, "Total:") ~ ($incoming + $outgoing) / 3600 ~ " hours"; $p1.put: ""; $p1.put: sprintf("%-*s", $fmt, "Declined:") ~ .grep( {$_.[2] eqv "Incoming Call" and $_.[4] == 0}).elems; $p1.put: sprintf("%-*s", $fmt, "Declined (they):") ~ .grep( {$_.[2] eqv "Outgoing Call" and $_.[4] == 0}).elems; $p1.put: sprintf("%-*s", $fmt, "Missed Calls:") ~ .grep(*.[2] eqv "Missed Call").elems; } } } ui.interact; ui.shutdown; } multi sub MAIN( Bool :$version #= print version ) is export { put "Lacerta v" ~ $?DISTRIBUTION.meta; }