commit e2facf9d06fe71e5992132faedcb80f2aac97901
Author: Remy Noulin <loader2x@gmail.com>
Date: Fri, 4 Jun 2021 20:24:16 +0200
bar chart
.gitignore | 63 ++++++++++++++++++++
README.md | 12 ++++
git-stats.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 269 insertions(+)
Diffstat:
| A | .gitignore | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | README.md | | | 12 | ++++++++++++ |
| A | git-stats.c | | | 194 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 269 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,63 @@
+# Vim
+*.sw*
+
+# Debug
+.gdb_history
+
+# Coverage
+*.gcov
+*.gcda
+*.gcno
+
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
diff --git a/README.md b/README.md
@@ -0,0 +1,12 @@
+This version creates a barchart:
+
+ █
+ █
+ █
+ ▂ █ ▂ █
+ ▂▂ ▄ █ █ ▇ █ █ █ ▄ █
+ ▂██▄█ ▅ ▂▅█▇ █▇ ▅ ██▄ █ ▅ █▂ █ █▅█▂ █ ██
+ █████▇██▄████▂██▄█▂█ ▂████▅█▄█▅ █▇▇ █▄ █ ██ ▇█ ████▅█▂▄██
+▄ ████████████████████▇▇██████████▂███ ▅██▂█▄██▅██▄███████████
+█ ▇▇ ▄████████████████████████████████████▄███████████████████████
+█▇▄██▄█████████████████████████████████████████████████████████████
diff --git a/git-stats.c b/git-stats.c
@@ -0,0 +1,194 @@
+#! /usr/bin/env sheepy
+/* or direct path to sheepy: #! /usr/local/bin/sheepy */
+/* Libsheepy documentation: https://spartatek.se/libsheepy/ */
+#include "libsheepyObject.h"
+
+// git commands to get statistics:
+// https://devtut.github.io/git/git-statistics.html
+
+int argc; char **argv;
+
+/* enable/disable logging */
+/* #undef pLog */
+/* #define pLog(...) */
+
+// terminal size type
+typ struct {u16 rows; u16 cols;} winSizet;
+
+/* terminal window size */
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+// get terminal window size
+winSizet wsize (void)
+{
+ struct winsize w;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+
+ return (winSizet){w.ws_row, w.ws_col};
+}
+
+int main(int ARGC, char** ARGV) {
+
+ argc = ARGC; argv = ARGV;
+
+ initLibsheepy(ARGV[0]);
+ setLogMode(LOG_DATE);
+ //openProgLogFile();
+ //setLogSymbols(LOG_UTF8);
+ //disableLibsheepyErrorLogs;
+
+
+ // Steps
+ // get git statistics
+ // sort the dates
+ // remove time, keep only the date in a slice: slDates
+ // count daily commits
+ // compute min and max daily count
+ // compute earliest and latest dates in unix time
+ // get window size
+ // aggregate daily count to window columns
+ // create a blank display array
+ // fill the display with the plot
+ // show plot
+
+ // get git statistics
+ cleanListP(dates) = execOut("git log --pretty=format:\"%ai\"");
+
+ var c = lenG(dates);
+
+ // sort the dates
+ sortG(&dates);
+
+ // remove time, keep only the date in a slice: slDates
+ // count daily commits
+ typ struct {
+ char *date;
+ int count;
+ } dateCountst;
+
+ sliceT(slDatest, dateCountst);
+ slDatest slDates;
+ sliceInit(&slDates);
+
+ dateCountst d = {0};
+ forEachS(dates, s) {
+ s[11] = 0;
+ if (not d.date) {
+ // first element
+ d.date = s;
+ inc d.count;
+ }
+ else {
+ if (eqG(d.date, s)) {
+ inc d.count;
+ }
+ else {
+ sliceAppend(&slDates, d);
+ d.date = s;
+ d.count = 1;
+ }
+ }
+ }
+ // append last date
+ sliceAppend(&slDates, d);
+
+ // compute min and max daily count
+ int minCount = sliceAt(&slDates, 0).count;
+ int maxCount = 0;
+
+ sliceForEach(&slDates, date) {
+ minCount = MIN(date->count, minCount);
+ maxCount = MAX(date->count, maxCount);
+ }
+
+ // compute earliest and latest dates in unix time
+ time_t earliest, latest;
+
+ earliest = strToUnixTime(sliceFirst(&slDates).date, "%Y-%m-%d");
+ latest = strToUnixTime(sliceLast (&slDates).date, "%Y-%m-%d");
+
+ //logI("%"PRIu64" %"PRIu64" = %f", earliest, latest, (f64)((latest-earliest)/86400)/365);
+
+ typ struct {
+ int col;
+ int value;
+ } pet; // plot element type
+
+ sliceT(plt, pet);
+ plt slPl;
+ sliceInit(&slPl);
+
+ // get window size
+ winSizet wz = wsize();
+ #define graphHeight 20
+
+
+ //logI("min %d, max %d, w %d h %d, first %s, last %s", minCount, maxCount, wz.cols, wz.rows, sliceFirst(&slDates).date, sliceLast(&slDates).date);
+
+ // aggregate daily count to window columns
+ pet p = {0};
+
+ sliceForEach(&slDates, date) {
+ time_t t = strToUnixTime(date->date, "%Y-%m-%d");
+ int col = (f64)(t-earliest)/(f64)(latest-earliest) * (f64)(wz.cols-1/*-1 to make sure it writes inside the display*/);
+ if (col == p.col) {
+ p.value = MAX(date->count, p.value);
+ }
+ else {
+ sliceAppend(&slPl, p);
+ p.col = col;
+ p.value = date->count;
+ }
+ }
+ sliceAppend(&slPl, p);
+
+ // create a blank display array
+ char *display[wz.cols][graphHeight];
+
+ char *BARS[] = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"};
+
+ // clear display
+ range(i, wz.cols) {
+ range(j, graphHeight) {
+ display[i][j] = BARS[0];
+ }
+ }
+
+
+ // fill the display with the plot
+ sliceForEach(&slPl, elm) {
+ int dpHeight = (f64)(graphHeight*8) - (f64)elm->value/(f64)maxCount * (f64)(graphHeight*8);
+ int bits = dpHeight & 7;
+ int cbar = 8 - bits;
+
+ // draw bar
+ int h = dpHeight / 8;
+
+ if (bits) {
+ // print BARS[cbar]
+ display[elm->col][h] = BARS[cbar];
+ inc h;
+ }
+
+ rangeFrom(i, h, graphHeight) {
+ display[elm->col][i] = BARS[8];
+ }
+
+ //logI("col %d, value %d", elm->col, elm->value);
+ //logI("col %3d, value %4d %3d cbar %d bits %d", elm->col, dpHeight, h, cbar, bits);
+ }
+
+ // show plot
+ range(j, graphHeight) {
+ range(i, wz.cols) {
+ printf(display[i][j]);
+ }
+ put;
+ }
+
+
+ sliceFree(&slPl);
+ sliceFree(&slDates);
+}
+// vim: set expandtab ts=2 sw=2: