From 20bf7ccb58553d0fbb1543099c59ec8a51726e6b Mon Sep 17 00:00:00 2001 From: Yoni Rabkin Date: Mon, 19 Oct 2015 17:15:34 -0400 Subject: Add rt-report. --- rt-liberation-report.el | 150 ++++++++++++++++++++++++++++++++++++++++++++++++ rt-liberation.el | 8 ++- 2 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 rt-liberation-report.el diff --git a/rt-liberation-report.el b/rt-liberation-report.el new file mode 100644 index 0000000..da1a703 --- /dev/null +++ b/rt-liberation-report.el @@ -0,0 +1,150 @@ +;;; rt-liberation-report.el --- Free from RT + +;; Copyright (C) 2015 Yoni Rabkin +;; +;; Authors: Yoni Rabkin +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public +;; License along with this program; if not, write to the Free +;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +;; MA 02111-1307, USA. + +;;; History: +;; +;; I wrote rt-report.py sometime in 2013 because people asked for some +;; information as to how many tickets were being resolved, and by +;; whom. When in came time up update rt-report.py I came to my senses +;; and decided to re-write it in Emacs Lisp as a part of +;; rt-liberation. + + +;;; Code: + +(require 'rt-liberation-rest) + +(defvar rt-liber-report-csv-header + '("date" "tickets resolved") + "Headers for comma separated value output.") + +(defun rt-liber-report-get-interval (rt-queue start-date end-date) + "Return tickets resolved between START-DATE and END-DATE. + +The tickets must have their current status be Resolved in order +to be returned by this function. If no tickets match the query, +return `nil'." + (when (or (not (stringp rt-queue)) + (not (stringp start-date)) + (not (stringp end-date))) + (error "bad argument/s")) + (rt-liber-rest-run-show-base-query + (rt-liber-rest-run-ls-query + (rt-liber-compile-query + (and (queue rt-queue) + (resolved end-date start-date) + (status "resolved")))))) + +(defun rt-liber-report-scan-ticket (ticket-alist) + "Convert TICKET-ALIST to set format." + (let ((date-resolved (cdr (assoc "Resolved" ticket-alist))) + (owner (cdr (assoc "Owner" ticket-alist)))) + `(,(float-time (date-to-time date-resolved)) . ,owner))) + +(defun rt-liber-report-scan-interval (interval) + "Convert the list of tickets into an ordered format." + (when (not interval) + (error "no tickets in interval")) + (let ((l (copy-tree interval)) + (r nil)) + (while l + (setq r (append r `(,(rt-liber-report-scan-ticket (car l))))) + (setq l (cdr l))) + ;; sort the list when it is still in seconds format + (setq r (sort r + #'(lambda (a b) + (< (car a) (car b))))) + ;; change the sorted list by day-date format, so that we can + ;; pigeon-hole count by day later on + (dolist (e r) + (setcar e (format-time-string "%Y-%m-%d" (car e)))) + r)) + +(defun rt-liber-report-count (f l) + "Apply function F to list L to produce a count." + (let (out) + (while l + (let* ((head (car l)) + (old-value (cdr (assoc (funcall f head) out)))) + (if old-value + (setcdr (assoc (funcall f head) out) (+ old-value 1)) + (setq out (append out `((,(funcall f head) . 1)))))) + (setq l (cdr l))) + out)) + +(defun rt-liber-report-count-total (l) + (let ((c 0)) + (while l + (setq c (+ c (cdr (car l)))) + (setq l (cdr l))) + c)) + +(defun rt-liber-report-count-by-date (l) + "Count resolved tickets by date." + (rt-liber-report-count #'car l)) + +(defun rt-liber-report-count-by-owner (l) + "Count resolved tickets by owner." + (rt-liber-report-count #'cdr l)) + +(defun rt-liber-report-print-csv (header l) + "Output list L in a CSV format, starting with HEADER." + (let (out) + (with-temp-buffer + (insert (format "\n%s\n" header)) + (dolist (entry l) + (insert + (format "%s, %s\n" (car entry) (cdr entry)))) + (setq out (buffer-string))) + out)) + +(defun rt-liber-report (rt-queue start-date end-date) + "Print tickets resolved between START-DATE and END-DATE." + (let ((tickets (rt-liber-report-scan-interval + (rt-liber-report-get-interval + rt-queue start-date end-date))) + by-date by-owner + by-date-out + by-owner-out + total) + (when (not tickets) + (error (concat "no tickets in interval between " + start-date " and " end-date))) + ;; collate + (setq by-date (rt-liber-report-count-by-date tickets) + by-owner (rt-liber-report-count-by-owner tickets)) + ;; rank owners by resolved tickets + (setq by-owner + (sort + by-owner + #'(lambda (a b) + (> (cdr a) (cdr b))))) + ;; count total + (setq total (rt-liber-report-count-total by-owner)) + ;; print + (insert (rt-liber-report-print-csv "date, resolved" by-date)) + (insert (rt-liber-report-print-csv "owner, resolved" by-owner)) + (insert (format "\ntotal tickets resolved: %d\n" total)))) + + +(provide 'rt-liberation-report) + +;;; rt-liberation-report.el ends here. diff --git a/rt-liberation.el b/rt-liberation.el index b9b013f..1a62a70 100644 --- a/rt-liberation.el +++ b/rt-liberation.el @@ -58,6 +58,9 @@ (defvar rt-liber-lastupdated-string "LastUpdated" "String representation of \"lastupdated\" query tag.") +(defvar rt-liber-resolved-string "Resolved" + "String representation of \"resolved\" query tag.") + (defvar rt-liber-content-string "Content LIKE" "String representation of \"content\" query tag.") @@ -244,7 +247,7 @@ This variable is made buffer local for the ticket history") email-address))) (defun rt-liber-time-p (sym) "Return t if SYM is a temporal attribute, otherwise nil." - (member sym '(created lastupdated))) + (member sym '(created lastupdated resolved))) (defun rt-liber-negation-p (sym) (member sym '(not))) @@ -291,7 +294,8 @@ AFTER date after predicate." ;; time ((rt-liber-time-p query) `,(cond ((equal query 'created) rt-liber-created-string) - ((equal query 'lastupdated) rt-liber-lastupdated-string))) + ((equal query 'lastupdated) rt-liber-lastupdated-string) + ((equal query 'resolved) rt-liber-resolved-string))) ((and (listp query) (rt-liber-time-p (car query))) `(rt-liber-make-interval -- cgit v1.2.3