- #!/usr/bin/env perl
-
- =pod
-
- =head1 NAME
-
- timeslice - aggregate CLI history, notes, blog entries, etc. for a given time
-
- =head1 SYNOPSIS
-
- # Find items for January 1st, 2019:
- timeslice 2019-01-01
-
- # Find items corresponding to a file's time - special cases some files
- # with an embedded date, like a blog entry or a vimwiki diary page,
- # otherwise falls back to mtime:
- timeslice --file ~/notes/vimwiki/diary/2019-01-02.wiki
- timeslice --file ~/screenshots/foo.png
-
- =head1 DESCRIPTION
-
- timeslice is a utility, for now extremely specific to my personal setup, for
- aggregating info from various systems by date. It currently looks up entries
- in vimwiki, L<p1k3|https://p1k3.com/> (my blog), Pinboard bookmarks, command
- line history using
- L<commandlog|https://code.p1k3.com/gitea/brennen/commandlog>, and commits in
- git repos registered with myrepos.
-
- This code is hot garbage.
-
- =head1 AUTHOR
-
- L<Brennen Bearnes|https://p1k3.com/>
-
- =cut
-
- use warnings;
- use strict;
-
- use 5.10.0;
-
- # This seems to be necessary to print UTF-8 without warnings:
- use open ':std', ':encoding(UTF-8)';
-
- use Carp;
- use Data::Dumper;
- use Encode qw(decode encode);
- use File::Basename;
- use File::Find;
- use File::HomeDir;
- use File::Spec;
- use Getopt::Long;
- use JSON;
- use LWP::Simple;
- use Pod::Usage;
- use Text::Wrap;
- use Time::Piece;
- use Time::Seconds;
-
- $Text::Wrap::columns = 72;
- my $ISO_8601_FMT = '%Y-%m-%d';
-
- # Handle options, including help generated from the POD above:
- my $date_from_file = 0;
- GetOptions(
- 'file' => \$date_from_file,
- 'help' => sub { pod2usage(0) },
- ) or pod2usage(2);
-
- if (! @ARGV) {
- pod2usage(0);
- }
-
- # If --file was invoked, get time from targetted file instead of taking
- # the first param as a date:
- my %dates;
- if ($date_from_file) {
- my $file = File::Spec->rel2abs($ARGV[0]);
- say "Date from file: $file";
-
- if ($file =~ m{^.*?/diary/(\d{4}-\d{2}-\d{2})[.]wiki$}) {
- # Handle vimwiki diary entries:
- $dates{$1} .= '(vimwiki)';
- } elsif ($file =~ m{^.*?/p1k3/archives/(\d{4})/(\d+)/(\d+)(/index)?$}) {
- # Handle p1k3 entries:
- $dates{ sprintf('%d-%02d-%02d', $1, $2, $3) } .= "(p1k3)";
- }
-
- # Just use the file's modification time:
- $dates{ get_mtime_day($ARGV[0]) } .= "(mtime)";
-
- my $git_date = git_commit_date($file);
- if ($git_date) {
- # Handle git commit dates:
- $dates{ $git_date } .= "(git commit)";
- }
- } else {
- $dates{ $ARGV[0] } .= "(arg)";
- }
-
- foreach my $date (keys %dates) {
- unless ($date =~ m/\d+-\d+-\d+/) {
- say "invalid date: $date";
- next;
- }
- my $day = Time::Piece->strptime($date, $ISO_8601_FMT);
- print_day_slice($day, $dates{$date});
- }
-
- sub print_day_slice {
- my ($day, $label) = @_;
- my $next_day = $day + ONE_DAY;
-
- say "${day} ${label} {{{";
-
- print_section('p1k3', p1k3($day));
- print_section('vimwiki', vimwiki($day));
- print_section('shell', commandlog($day, $next_day));
- print_section('pinboard', pinboard($day));
- print_section('myrepos', myrepos($day, $next_day));
-
- say "}}}";
- }
-
- sub print_section {
- my ($title, @content) = @_;
- return unless defined $content[0];
-
- say "${title} {{{";
- foreach my $thing (@content) {
- say $thing;
- }
- say "}}}";
- }
-
- sub commandlog {
- my ($day, $next_day) = @_;
- my $after = $day->strftime('%Y-%m-%d 00:00:00');
- my $before = $next_day->strftime('%Y-%m-%d 00:00:00');
- my (@cl_lines) = `commandlog log --after="${after}" --before="${before}"`;
- if (@cl_lines > 1) {
- return join "", @cl_lines;
- }
- return;
- }
-
- sub myrepos {
- my ($day, $next_day) = @_;
- my $after = $day->strftime('%Y-%m-%d 00:00:00');
- my $before = $next_day->strftime('%Y-%m-%d 00:00:00');
- my (@mr_lines) = `cd ~ && mr -m log --stat --since="${after}" --before="${before}" 2>&1`;
- if (@mr_lines > 1) {
- return join "", @mr_lines;
- }
- return;
- }
-
- sub p1k3 {
- my ($day) = @_;
- my $daypath = $day->strftime(
- "%Y/%-m/%-d"
- );
- if (-e File::HomeDir->my_home . '/p1k3/archives/' . $daypath) {
- return join "", `cd ~/p1k3 && wrt display ${daypath} | lynx -stdin -dump`
- }
- return;
- }
-
- sub pinboard {
- my ($day) = @_;
-
- # See the bit about auth here:
- # https://pinboard.in/api/
- my $home = File::HomeDir->my_home;
- my $token = file_get_contents("${home}/.pinboardrc");
- chomp($token);
-
- my $url = $day->strftime(
- 'https://api.pinboard.in/v1/posts/get?dt=%Y-%m-%d&meta=yes&format=json&auth_token='
- . $token
- );
-
- my $pinboard_json = `curl -s '${url}'`;
- my $JSON = JSON->new->utf8->pretty;
- my $pinboard_hashref = $JSON->decode($pinboard_json);
- my @posts = @{ $pinboard_hashref->{posts} };
-
- my @output;
- # my @output = (
- # "source: curl -s -K ${home}/.pinboardrc '${url}'"
- # );
- foreach my $post (@posts)
- {
- my $display = wrap("", "", $post->{description}) . "\n"
- if length($post->{description});
-
- $display .= wrap("\t", "\t", $post->{extended}) . "\n"
- if length($post->{extended});
-
- $display .= "\t" . $post->{href} . "\n";
-
- $display .= wrap("\t", "\t", $post->{tags}) . "\n"
- if length($post->{tags});
-
- push @output, $display;
- }
-
- if (@output > 0) {
- return join "\n", @output;
- }
- return;
- }
-
- sub vimwiki {
- my ($day) = @_;
- my $daypath = $day->strftime(
- File::HomeDir->my_home . "/notes/vimwiki/diary/%Y-%m-%d.wiki"
- );
- if (-e $daypath) {
- return ("source: $daypath", file_get_contents($daypath));
- }
- }
-
- # PHP-style file-content grabbing:
- sub file_get_contents {
- my ($file) = @_;
-
- open my $fh, '<', $file
- or croak "Couldn't open $file: $!\n";
-
- my $contents;
- {
- # line separator:
- local $/ = undef;
- $contents = <$fh>;
- }
-
- close $fh or croak "Couldn't close $file: $!";
-
- # TODO: _May_ want to assume here that any file is UTF-8 text.
- # http://perldoc.perl.org/perlunitut.html
- # return decode('UTF-8', $contents);
- return $contents;
- }
-
- # Horked from WRT::Date.
- sub get_mtime_day
- {
- use POSIX qw(strftime);
- my ($file) = @_;
-
- # my( $dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
- # $atime, $mtime, $ctime, $blksize, $blocks )
- # = stat( $filename );
-
- my $mtime = (stat $file)[9];
- # return strftime("%Y-%m-%dT%H:%M:%SZ", localtime($mtime));
- return strftime("%Y-%m-%d", localtime($mtime));
- }
-
- # Get a commit date for a given path:
- sub git_commit_date {
- my ($path) = @_;
- my $dirname = dirname($path);
- say $path;
-
- if (in_git_worktree($path)) {
- my ($git_output) = `cd ${dirname} && git log -1 --format=%ci "${path}"`;
- my ($date) = $git_output =~ m/^(\d{4}-\d{2}-\d{2}).*$/;
-
- if ($date) {
- return $date;
- }
- }
- return;
- }
-
- # Is a given path in a git working tree?
- sub in_git_worktree {
- my ($path) = @_;
- my $dirname = dirname($path);
-
- return 0 unless -d "$dirname/.git";
-
- my ($git_check) = `cd "${dirname}" && git rev-parse --is-inside-work-tree`;
- if (defined $git_check) {
- chomp($git_check);
- return 1 if $git_check eq 'true';
- }
- return 0;
- }
|