root/trunk/projects/bos/web/reports-xml-handler.lisp

Revision 3681, 5.4 kB (checked in by ksprotte, 4 months ago)

checkpoint

Line 
1 (in-package :bos.web)
2
3 (enable-interpol-syntax)
4
5 (defclass reports-xml-handler (prefix-handler)
6   ())
7
8 (defvar *report-generators* (make-hash-table))
9 (defvar *contracts-to-process*)
10 (defvar *year*)
11 (defvar *month-names* '("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
12
13 (defmethod cxml:unparse-attribute ((value (eql nil)))
14   "false")
15
16 (defmethod cxml:unparse-attribute ((value (eql t)))
17   "true")
18
19 (defmacro defreport (name arguments &body body)
20   `(setf (gethash ',name *report-generators*) (lambda (,@arguments) ,@body)))
21
22 (defun contract-year (contract)
23   (multiple-value-bind (second minute hour date month year day-of-week is-dst tz) (decode-universal-time (contract-date contract))
24     (declare (ignore second minute hour date month day-of-week is-dst tz))
25     year))
26
27 (defmethod handle ((handler reports-xml-handler))
28   (with-xml-response (:root-element "response")
29     (destructuring-bind (name &optional *year* &rest arguments) (decoded-handler-path handler)
30       (setf *year* (and *year* (parse-integer *year*)))
31       (let ((*contracts-to-process* (sort (remove-if (lambda (contract)
32                                                        (or (not (contract-paidp contract))
33                                                            (and *year*
34                                                                 (not (eql *year* (contract-year contract))))))
35                                                      (all-contracts))
36                                           #'< :key #'contract-date)))
37         (setf name (intern (string-upcase name) :bos.web))
38         (apply (or (gethash name *report-generators*)
39                    (error "invalid report name ~A" name))
40                arguments)))))
41
42 (defun all-contracts/internal (&key include-coords)
43   (dolist (contract *contracts-to-process*)
44     (with-element "contract"
45       (attribute "id" (store-object-id contract))
46       (attribute "sponsor-id" (store-object-id (contract-sponsor contract)))
47       (attribute "universal-time" (contract-date contract))
48       (attribute "paid" (contract-paidp contract))
49       (attribute "date-time" (format-date-time (contract-date contract) :xml-style t))
50       (attribute "country" (sponsor-country (contract-sponsor contract)))
51       (attribute "sqm-count" (length (contract-m2s contract)))
52       (when include-coords
53         (dolist (m2 (contract-m2s contract))
54           (with-element "m2"
55             (attribute "utm-x" (m2-x m2))
56             (attribute "utm-y" (m2-y m2))))))))
57
58 (defreport all-contracts ()
59   (all-contracts/internal))
60
61 (defreport all-contracts-m2s ()
62   (all-contracts/internal :include-coords t))
63
64 (defun week-of-contract (contract)
65   "Return Week key (YYYY-WW) for given contract."
66   (multiple-value-bind (second minute hour date month year day-of-week is-dst tz) (decode-universal-time (contract-date contract))
67     (declare (ignore second minute hour day-of-week is-dst tz))
68     (multiple-value-bind (week-no week-year)
69         (week-of-year year month date)
70       (when (and (> week-no 50)
71                  (eql month 1))
72         ;; If the date falls within the last week of the previous
73         ;; year, we put it into the first week of the current year in
74         ;; order to simplify graphics drawing.
75         (setf week-no 1))
76       (format nil "~A-~A" week-year week-no))))
77
78 (defun week-first-yday (contract)
79   "Return the day-of year of the first day of the contract's date"
80   (multiple-value-bind (second minute hour date month year day-of-week is-dst tz) (decode-universal-time (contract-date contract))
81     (declare (ignore second minute hour day-of-week is-dst tz))
82     (max 0 (- (day-of-year year month date) (day-of-week year month date)))))
83
84 (defreport contracts-by-week ()
85   (dolist (week-contracts (group-on *contracts-to-process*
86                                     :test #'equal
87                                     :key #'week-of-contract))
88     (with-element "week"
89       (attribute "week-first-yday" (week-first-yday (first (cdr week-contracts))))
90       (attribute "key" (first week-contracts))
91       (attribute "contracts" (length (cdr week-contracts)))
92       (attribute "sqms" (apply #'+ (mapcar (lambda (contract) (length (contract-m2s contract))) (cdr week-contracts))))))
93   (dotimes (month 12)
94     (with-element "month"
95       (attribute "number" month)
96       (attribute "name" (nth month *month-names*))
97       (attribute "start-yday" (1- (day-of-year *year* (1+ month) 1))))))
98
99 (defreport contract-sizes ()
100   (let ((contract-sizes (make-hash-table :test #'equal))
101         (thresholds '(1 10 30 100 10000000)))
102     (dolist (threshold thresholds)
103       (setf (gethash threshold contract-sizes) 0))
104     (dolist (contract *contracts-to-process*)
105       (dolist (threshold thresholds)
106         (when (<= (length (contract-m2s contract)) threshold)
107           (incf (gethash threshold contract-sizes))
108           (return))))
109     (dolist (threshold thresholds)
110       (with-element "contracts"
111         (attribute "threshold" threshold)
112         (attribute "count" (gethash threshold contract-sizes))))))
113
114 (defreport contract-countries ()
115   (dolist (country-contracts (sort (group-on *contracts-to-process*
116                                              :test #'equal
117                                              :key (lambda (contract) (sponsor-country (contract-sponsor contract))))
118                                    #'> :key (lambda (entry) (length (cdr entry)))))
119     (with-element "country"
120       (attribute "code" (car country-contracts))
121       (attribute "contracts" (length (cdr country-contracts))))))
Note: See TracBrowser for help on using the browser.