working new users per day
[kspaans/nntp-to-dot] / userstats.ss
1 ;; Copyright (C) 2009  Kyle Spaans
2 ;;  this program is distributed under the GPL v2 or (at your option)
3 ;;  any later version.
4
5 ;; Calculate a bunch of user statistics:
6 ;; - Number of users
7 ;; - Post counts of users
8 ;; - Average number of posts
9 ;; - posts per day...
10 ;; - new users versus time...
11
12 #lang scheme
13
14 (require "common.ss")
15 (require srfi/19)
16
17 (provide count-users new-u-vs-time)
18
19 ;; User Statistics Struct
20 ;; String - usename in the form of an email address, possibility of duplicates
21 ;; int    - total number of posts in newsgroup
22 ;; date   - SRFI-19 date struct, date of first post to group
23 ;; date   - "     "    ", date of last post to struct
24 ;; int    - number of days in range from first post day to past post day
25 ;; real   - number of posts / number of days
26 (define-struct ustats (user nump firstp lastp days avgppd))
27
28 ;; Collect stats in the form of '(USERNAME ustats)
29 (define users (make-hash))
30
31 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
32
33 ;; print-stats: void -> void
34 ;; Pretty print the contents of the user statistics table to stdout
35 (define (print-stats)
36   (printf "Total number of users: ~a~n~n" (hash-count users))
37   (hash-for-each
38     users
39     (lambda (k v)
40       (printf "User: ~a~nCount: ~a~nFirst: ~a~nLast:  ~a~nAvgPPD: ~a~n~n"
41               (ustats-user v)
42               (ustats-nump v)
43               (ustats-firstp v)
44               (ustats-lastp v)
45               (ustats-avgppd v)))))
46
47 ;; count-users: int int newsgroup -> void
48 ;; build a hash table with each user from the group collecting stats:
49 ;; - number of posts
50 ;; - first/last post
51 ;; So if "last" post is empty, then user has only 1 post
52 (define (count-users first last newsd)
53   (cond
54     [(= first last) (print-stats)]
55     [else (letrec [(message (message-getter newsd first (list from-regexp date-regexp)))
56                    (mesg-from (if (boolean? message) message (car message)))
57                    (mesg-date (if (boolean? message) message (cadr message)))]
58             (cond
59               [(boolean? message) (void)]
60               [else
61                (let [(result (hash-ref users mesg-from #f))]
62                  (cond
63                    [(boolean? result)
64                     ;(printf "First post ~a ::@:: ~a~n" mesg-from mesg-date)
65                     (hash-set! users mesg-from (make-ustats mesg-from 1 (get-date mesg-date) '() 1 1))]
66                    [else
67                                      ;; Calculate the ordinal day of the year (1-365) of the given date
68                                      ;; then subtract. Alternatively date->time and time-difference
69                                      ;; functions can be used to calculate this
70                     (let [(pdays (if (empty? (ustats-lastp result))
71                                      1
72                                      (+ 1 (- (date-year-day (ustats-lastp result))
73                                              (date-year-day (ustats-firstp result))))))
74                           (new-nump (+ 1 (ustats-nump result)))]
75                     (hash-set! users mesg-from (make-ustats
76                                                  mesg-from
77                                                  new-nump
78                                                  (ustats-firstp result)
79                                                  (get-date mesg-date)
80                                                  pdays
81                                                  (/ new-nump pdays))))]))]))
82           (count-users (+ first 1) last newsd)]))
83
84 ;; new-u-vs-time: void -> void
85 ;; Prints out info (similar to PPD) representing the number of new users showing up
86 ;; on the newsgroup (e.g. first posts) plotted versus time. This will be plottable with GNUPLOT.
87 ;; First map all first post instances to a new hash table that will count the number of first
88 ;; posts on each day. Then map over that hash table to print them out appropriately.
89 (define (new-u-vs-time)
90   (let [(new-u-hash (make-hash))]
91     (hash-for-each
92       users
93       (lambda (k v)
94         (letrec [(ndate (date->string (ustats-firstp v) "~D"))
95                  (result (hash-ref new-u-hash ndate #f))]
96           (cond
97             [(boolean? result) (hash-set! new-u-hash ndate 1)]
98             [else (hash-set! new-u-hash ndate (+ 1 result))]))))
99     (hash-for-each new-u-hash
100                    (lambda (k v) (printf "~a ~a~n" k v)))))