;;; -*- mode: Lisp; Syntax: Common-Lisp; -*- ;;; ;;; Copyright (c) 2009 by the authors. ;;; ;;; See LICENCE for details. (in-package :hu.dwim.number-toss) ;;;;;; ;;; Chart emitter (def function statistics/emit-data (label legend-labels records) `str("var data = new google.visualization.DataTable(); data.addColumn('string', '" ,label "');" ,(iter (for legend-label :in legend-labels) `str("data.addColumn('number', '" ,legend-label "');")) "data.addRows([" ,(iter (for record :in records) `str("['" ,(princ-to-string (first record)) "'" ,(iter (for element :in (rest record)) `str(", " ,(princ-to-string element))) "], ")) "]);")) (def function statistics/emit-line-chart (id title x-label y-label legend-labels records &key (color "darkblue") (log-scale #f)) `str("google.load(\"visualization\", \"1\", {packages:[\"corechart\"]}); google.setOnLoadCallback(draw" ,id "); function draw" ,id "() {" ,(statistics/emit-data x-label legend-labels records) "var options = {width: 1000, height: 240, title: '" ,title "', colors: ['" ,color "'], hAxis: {title: '" ,x-label "', slantedText: false}, vAxis: {title: '" ,y-label "', logScale: " ,(if log-scale "true" "false") "}, legend: {position: 'none'}}; var chart = new google.visualization.LineChart(document.getElementById('" ,id "')); chart.draw(data, options); }")) (def function statistics/emit-pie-chart (id title label legend-labels data) `str("google.load(\"visualization\", \"1\", {packages:[\"corechart\"]}); google.setOnLoadCallback(draw" ,id "); function draw" ,id "() {" ,(statistics/emit-data label legend-labels data) "var options = { width: 450, height: 300, title: '" ,title "' }; var chart = new google.visualization.PieChart(document.getElementById('" ,id "')); chart.draw(data, options); }")) ;;;;;; ;;; Statistics collector (def function statistics/collect-count (growth timestamps counter-function) (iter (for timestamp :in timestamps) (adjust-timestamp! timestamp (:set :hour 0) (:set :minute 0) (:set :sec 0) (:set :nsec 0))) (setf timestamps (sort (remove-duplicates timestamps :test 'timestamp=) 'timestamp<)) (iter (for timestamp :in timestamps) (for timestamp-begin = (if growth (first timestamps) (or timestamp (first timestamps)))) (for timestamp-end = (adjust-timestamp timestamp (:offset :day 1))) (collect (list (format-timestring nil timestamp :format '((:month 2) #\/ (:day 2))) (funcall counter-function timestamp-begin timestamp-end))))) (def function statistics/collect-player-count (growth) (statistics/collect-count growth (select ((registered-at-of player)) (from (player player)) (order-by :asc (registered-at-of player))) (lambda (timestamp-begin timestamp-end) (first-elt (select ((count player)) (from (player player)) (where (and (timestamp<= timestamp-begin (registered-at-of player)) (timestamp<= (registered-at-of player) timestamp-end)))))))) (def function statistics/collect-game-count (growth) (statistics/collect-count growth (select ((submitted-at-of game)) (from (game game)) (order-by :asc (submitted-at-of game))) (lambda (timestamp-begin timestamp-end) (first-elt (select ((count game)) (from (game game)) (where (and (timestamp<= timestamp-begin (submitted-at-of game)) (timestamp<= (submitted-at-of game) timestamp-end)))))))) (def function statistics/collect-challenge-count (growth) (statistics/collect-count growth (select ((submitted-at-of challenge)) (from (challenge challenge)) (order-by :asc (submitted-at-of challenge))) (lambda (timestamp-begin timestamp-end) (first-elt (select ((count challenge)) (from (challenge challenge)) (where (and (timestamp<= timestamp-begin (submitted-at-of challenge)) (timestamp<= (submitted-at-of challenge) timestamp-end)))))))) (def function statistics/collect-player-count-per-game-count (step) (bind ((records (append (select (0 player) (from (player player)) (where (= 0 (length (games-of player))))) (select ((count game) (player-of game)) (from (game game)) (group-by (player-of game)) (order-by :asc (count game)))))) (iter (for game-count :from 0 :to (first-elt (last-elt records)) :by step) (collect (list game-count (funcall 'count game-count records :key #'first :test (lambda (count element) (<= count element (+ count step -1))))))))) (def function statistics/collect-version-count () (select ((version-of player) (count player)) (from (player player)) (group-by (version-of player)))) (def function statistics/collect-country-count () (subseq (mapcar (lambda (record) (list (second (find (first record) +countries+ :key 'first :test 'equal)) (second record))) (select ((country-code-of player) (count player)) (from (player player)) (group-by (country-code-of player)) (order-by :descending (count (country-code-of player))))) 0 10)) (def function statistics/collect-usage-interval-count () (bind ((usage-intervals (select ((ceiling (timestamp-difference (last-started-at-of player) (registered-at-of player)) +seconds-per-day+)) (from (player player))))) (sort (iter (for usage-interval :in-sequence (remove-duplicates usage-intervals)) (collect (list usage-interval (funcall 'count usage-interval usage-intervals)))) #'< :key #'first)))