| 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)))))) |
|---|