root/generate-git-svg.scm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
(define-module (generate-git-svg)
  #:use-module (srfi srfi-1) ;; map/reduce
  #:use-module (srfi srfi-19) ;; date
  #:use-module (srfi srfi-69) ;; hash-tables
  #:use-module (generate-svg-gregorian)
  #:use-module (generate-svg-discord)
  #:use-module (system-utils)
  #:export (generate-svg
            generate-days-table))

(define (day-up-project day project)
  (if (hash-table? day)
      (begin
        (hash-table-set! day
                         project
                         (+ 1 (hash-table-ref/default day
                                                      project
                                                      0)))
        day)
      (let ((table (make-hash-table)))
        (hash-table-set! table project 1)
        table)))

(define (day-total-count day)
  (if (hash-table? day)
      (hash-table-fold day (lambda (k v acc) (+ v acc)) 0)
      0))

(define (unix->day unix)
  (date-year-day (time-monotonic->date (make-time 'time-monotonic 0 unix))))

(define (git-log-str directory start end)
  (cdr (with-error-to-string*
        (lambda ()
          (shell-command-to-string*
           "git" "--git-dir" directory
           "log" "--pretty=format:%at"
           (string-append "--after=" start)
           (string-append "--before=" end))))))

(define (get-dates year directory)
  (let* ((start-date (make-date 0 0 0 0 1 1 year 0))
         (end-date (make-date 0 0 0 0 31 12 year 0))
         (start (date->string start-date "~s"))
         (end (date->string end-date "~s"))
         (result-str (git-log-str directory start end)))
    ;; shitty workaround that allows giving "repo/.git" or "repo"
    (if (string-null? result-str)
        (let ((non-bare-str
               (git-log-str
                (string-append directory file-name-separator-string ".git")
                start
                end)))
          (unlines non-bare-str))
        (unlines result-str))))

(define (unlines str) (if (string-null? str)
                          '()
                          (string-split str #\newline)))

(define* (generate-days-table year directory #:optional (days (make-hash-table)))
  (let* ((dates-str (get-dates year directory))
         (dates-number (map unix->day (map string->number dates-str))))
    (map (lambda (d)
           (hash-table-set! days d
                            (day-up-project
                             (hash-table-ref/default days d 0) directory))
           d)
         dates-number)
    days))

(define (projectname dir)
  (let ((name (basename dir)))
    (if (string=? name ".git")
        (basename (dirname dir))
        name)))

(define (make-tooltip day)
  (if (hash-table? day)
      (hash-table-fold day (lambda (k v acc)
                             (string-append
                              (if acc (string-append acc "\n") "")
                              (projectname k) ": " (number->string v)))
                       #f)
      "no commits"))

(define (rgba-number-str v)
  (number->string (inexact->exact (floor v))))
(define (rgba-string r g b a)
  (string-append "rgba(" (rgba-number-str r)
                 "," (rgba-number-str g)
                 "," (rgba-number-str b)
                 "," (number->string (exact->inexact  a)) ")"))

(define (generate-svg gen-fn year days)
  (let* ((max-val (hash-table-fold days (lambda (k v acc) (max (day-total-count v) acc)) 1)))
    (gen-fn
     year
     (lambda (date)
       (let* ((d (date-year-day date))
              (val (day-total-count (hash-table-ref/default days d #f)))
              (factor (exact->inexact (/ val max-val)))
              (tooltip (make-tooltip (hash-table-ref/default days d #f))))
         `(,(if (> val 0)
              `(fill ,(rgba-string (* factor 10)
                                   (* factor 245)
                                   (* factor 30)
                                   (if (> val 0) 1 0.8)))
              (if (eq? gen-fn generate-svg-discord)
                  '(fill "rgba(255,255,0,1)")
                  '(fill "rgba(100,100,100,0.9)")))
           ,(if (> val 0)
              `(class "day")
              `(class "day no-event"))
           (title ,tooltip)))))))