root/generate-data-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
116
117
(define-module (generate-data-svg)
  #:use-module (srfi srfi-1) ;; map/reduce
  #:use-module (srfi srfi-19) ;; date
  #:use-module (srfi srfi-69) ;; hash-tables
  #:use-module (system-utils)
  #:export (generate-svg
            generate-days-table))

(define* (generate-days-table year data #:optional (days (make-hash-table)))
  (map (lambda (d)
         (define date-id (date->id (assoc-ref d 'date)))
         (define current-activity-counter (hash-table-ref/default days date-id (make-hash-table)))
         (define project (assoc-ref d 'project))
         (define link (assoc-ref d 'link))
         (hash-table-set! days date-id
                          (increase-day-activity-project
                           current-activity-counter
                           project
                           link))
         date-id)
       data)
  days)

(define (increase-day-activity-project day project link)
  (if (hash-table? day)
      (let ()
        (define project-state (hash-table-ref/default day project (new-project-state link)))
        (define old-value (hash-table-ref project-state 'counter))
        (define old-links (hash-table-ref project-state 'links))
        (when link
          (hash-table-set! project-state 'links (cons link old-links)))
        (hash-table-set! project-state 'counter (+ 1 old-value))
        (hash-table-set! day project project-state)
        day)

      (let ((table (make-hash-table)))
        (hash-table-set! table project (new-project-state link))
        table)))

(define (new-project-state link)
  (alist->hash-table
   `((counter . 1)
     ,(if link
          `(links . (,link))
          `(links . ())) )))

(define (make-tooltip day)
  (if (hash-table? day)
      (hash-table-fold day (lambda (project project-state acc)
                             (define activity-count (hash-table-ref project-state 'counter))
                             (string-append
                              (if acc (string-append acc "\n") "")
                              (or project "") ": " (number->string activity-count)))
                       #f)
      "no activity"))

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

(define (day-total-links day)
  (if (hash-table? day)
      (hash-table-fold day (lambda (k v acc)
                             (define links (hash-table-ref v 'links))
                             (if (null? links)
                                 acc
                                 (append links acc)))
                       '())
      '()))


(define (date->id date)
  (string-append (number->string (date-year date))
                 "-"
                 (number->string (date-year-day date))))


(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 template year days)
  (define gen-fn (assoc-ref template 'fn))
  (define theme (assoc-ref template 'theme))
  (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->id date))
              (val (day-total-count (hash-table-ref/default days d #f)))
              (links (day-total-links (hash-table-ref/default days d #f)))
              (factor (/ 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)))
              `(fill ,(assoc-ref theme 'day-bg)))
           ,(if (> val 0)
              `(class "day")
              `(class "day no-event"))
           (title ,tooltip)
           ,(if (null? links)
                `(_ . _)
                `(desc ,(links-to-text (delete-duplicates links))))))))))

(define (links-to-text links)
  (if (null? links)
      ""
      (string-append "link: " (car links) "\n"
                     (links-to-text (cdr links)))))