git.fiddlerwoaroof.com
Browse code

misc: changes

Ed Langley authored on 31/07/2019 18:39:08
Showing 6 changed files
... ...
@@ -3,19 +3,51 @@
3 3
 ; curl -u user:password https://atomampd.atlassian.net/rest/api/2/issue/ATOMOS-212 | jq .
4 4
 (defparameter *hostname* "https://atomampd.atlassian.net")
5 5
 (defparameter *endpoint* (princ-to-string (puri:merge-uris *hostname* "/rest/api/2/")))
6
+(defparameter *agile-endpoint* (princ-to-string (puri:merge-uris *hostname* "/rest/agile/1.0/")))
6 7
 
7
-(defun update-hostname (new-subdomain)
8
-  (setf *hostname* (format nil "https://~a.atlassian.net" new-subdomain))
9
-  (setf *endpoint* (princ-to-string (puri:merge-uris "/rest/api/2/" *hostname*))))
8
+(defun update-hostname ()
9
+  (setf *hostname* "https://jira.cnvrmedia.net")
10
+  (setf *endpoint* (princ-to-string (puri:merge-uris "/rest/api/2/" *hostname*)))
11
+  (setf *agile-endpoint* (princ-to-string (puri:merge-uris *hostname* "/rest/agile/1.0/"))))
12
+
13
+(define-condition jira-error ()
14
+  ())
15
+
16
+(define-condition auth-call-unauthorized (jira-error)
17
+  ())
18
+
19
+(define-condition server-error ()
20
+  ())
10 21
 
11 22
 (defun api-get-call (auth method &rest parameters)
12 23
   "Connect to a GET REST endpoint specified by method and return a stream from
13 24
    which the response can be read."
14 25
   (let ((drakma:*text-content-types* (acons "application" "json" drakma:*text-content-types*)))
15
-    (drakma:http-request (puri:merge-uris method *endpoint*)
16
-                         :parameters (alexandria:plist-alist parameters)
17
-                         :basic-authorization auth
18
-                         :want-stream t)))
26
+    (format t "~&~a ~s~%" (puri:merge-uris method *endpoint*) (alexandria:plist-alist parameters))
27
+    (multiple-value-bind (stream retcode)
28
+        (drakma:http-request (puri:merge-uris method *endpoint*)
29
+                             :parameters (alexandria:plist-alist parameters)
30
+                             :basic-authorization auth
31
+                             :want-stream t)
32
+      (case retcode
33
+        (401 (error 'auth-call-unauthorized))
34
+        (500 (error 'server-error))
35
+        (t stream)))))
36
+
37
+(defun agile-get-call (auth method &rest parameters)
38
+  "Connect to a GET REST endpoint specified by method and return a stream from
39
+   which the response can be read."
40
+  (let ((drakma:*text-content-types* (acons "application" "json" drakma:*text-content-types*)))
41
+    (format t "~&~a ~s~%" (puri:merge-uris method *agile-endpoint*) (alexandria:plist-alist parameters))
42
+    (multiple-value-bind (stream retcode)
43
+        (drakma:http-request (puri:merge-uris method *agile-endpoint*)
44
+                             :parameters (alexandria:plist-alist parameters)
45
+                             :basic-authorization auth
46
+                             :want-stream t)
47
+      (case retcode
48
+        (401 (error 'auth-call-unauthorized))
49
+        (500 (error 'server-error))
50
+        (t stream)))))
19 51
 
20 52
 (defun api-post-call (auth method post-data)
21 53
   "Connect to a GET REST endpoint specified by method and return a stream from
... ...
@@ -4,7 +4,7 @@
4 4
   :description "Describe jira-api here"
5 5
   :author "Your Name <your.name@example.com>"
6 6
   :license "Specify license here"
7
-  :depends-on (#:fwoar.lisputils
7
+  :depends-on (#:fwoar-lisputils
8 8
                #:alexandria
9 9
                #:serapeum
10 10
                #:drakma
... ...
@@ -1,13 +1,31 @@
1 1
 ;;;; jira-api.lisp
2 2
 (in-package #:jira-api)
3 3
 
4
-(defun get-issues (auth)
5
-  (api-get-call auth "search"))
4
+(defun get-filter (auth filter &rest r &key (expand "subscriptions[:-5]"))
5
+  (declare (ignore expand))
6
+  (apply 'api-get-call auth (format nil "filter/~d" filter) r))
7
+
8
+(defun get-issues (auth &key jql)
9
+  (if jql
10
+      (api-get-call auth "search" "jql" jql)
11
+      (api-get-call auth "search")))
12
+
13
+(defmacro jql (auth &body jql)
14
+  `(api-get-call ,auth "search" "jql"
15
+                 ,(apply #'serapeum:concat jql)))
16
+
17
+(defun run-filter (auth filter)
18
+  (let ((jql (gethash "jql" (yason:parse (get-filter auth filter)))))
19
+    (get-issues auth :jql jql)))
6 20
 
7 21
 (defun get-issue (auth key &rest params &key expand)
8 22
   (declare (ignore expand))
9 23
   (apply 'api-get-call auth (format nil "issue/~a" key) params))
10 24
 
25
+(defun get-board-issues (auth key &rest params &key expand)
26
+  (declare (ignore expand))
27
+  (apply 'api-get-call auth (format nil "board/~a/issue" key) params))
28
+
11 29
 (defun get-projects (auth)
12 30
   (api-get-call auth "issue/createmeta"))
13 31
 
... ...
@@ -71,20 +89,41 @@
71 89
                    finally (push issue (gethash thing classes)))
72 90
           finally (return classes))))
73 91
 
74
-(defun show-issue-short (issue)
92
+(defun status-to-num (status)
93
+  (string-case (string-downcase status)
94
+    ("open" 0)
95
+    ("in progress" 1)
96
+    ("needs qr" 2)
97
+    ("needs demo" 3)
98
+    ("closed" 4)
99
+    (t 5)))
100
+
101
+(defun sort-issues-by-status (sheeple-issues)
102
+  (prog1 sheeple-issues
103
+    (sheeple:with-properties (issues) sheeple-issues
104
+      (setf issues
105
+            (sort issues #'< :key (op (status-to-num (name (status (fields _))))))))))
106
+
107
+(defun show-issue-short (issue &optional (stream t))
75 108
   (sheeple:with-properties (key self) issue
76 109
     (let* ((fields (fields issue))
77 110
            (status (name (status fields)))
78 111
            (summary (summary fields)))
79 112
 
80
-      (format t "~&~a (~a) <~a>~%"
113
+      (format stream "~&~a (~a) <~a>~%"
81 114
               key
82 115
               status 
83 116
               (puri:merge-uris (format nil "/browse/~a" (key issue))
84 117
                                *hostname*))
85 118
 
86
-      (show-summary summary)
87
-      (fresh-line))))
119
+      (show-summary summary stream)
120
+      (fresh-line stream))))
121
+
122
+(defun map-issues (fun sheeple-issues)
123
+  (sheeple:with-properties (issues) sheeple-issues
124
+    (loop for issue across issues
125
+          do (ensure-parent issue =issue=)
126
+          collect (funcall fun issue))))
88 127
 
89 128
 (defun show-issues (sheeple-issues)
90 129
   (sheeple:with-properties (issues) sheeple-issues
... ...
@@ -100,3 +139,17 @@
100 139
     (loop for project across projects
101 140
           do (ensure-parent project =project=)
102 141
           do (print-on-own-line (show project)))))
142
+
143
+(defun get-points-for-issues (count start)
144
+  (loop with end = (+ count start)
145
+     for batch-start from start to end by 50
146
+     for batch-count = (min (- end batch-start)
147
+                            50)
148
+     append (map 'list #'points
149
+                 (issues
150
+                  (json2sheeple
151
+                   (get-issues *auth* :jql
152
+                               (format nil "issueKey in (~{CJPM-~a~^, ~})"
153
+                                       (alexandria:iota batch-count
154
+                                                        :start batch-start)))
155
+                   =issues=)))))
... ...
@@ -1,13 +1,19 @@
1 1
 ;;;; package.lisp
2 2
 
3 3
 (defpackage #:jira-api.cli
4
-  (:use :cl :anaphora :alexandria :serapeum :fw.lu)
5
-  (:export #:prompt-for-lines
4
+  (:use :cl #:anaphora #:alexandria #:serapeum #:fw.lu)
5
+  (:export #:optional-prompt
6
+           #:prompt
6 7
            #:prompt-for-line
7
-           #:optional-prompt
8
-           #:prompt))
8
+           #:prompt-for-lines))
9 9
 
10 10
 (defpackage #:jira-api
11 11
   (:shadow #:comment)
12
-  (:use #:cl #:serapeum #:alexandria #:fw.lu #:jira-api.cli))
12
+  (:use #:cl #:jira-api.cli #:alexandria #:fw.lu #:serapeum)
13
+  (:export #:auth-call-unauthorized
14
+           #:classify-issues
15
+           #:get-issues
16
+           #:jql
17
+           #:jira-error
18
+           #:server-error))
13 19
 
... ...
@@ -6,7 +6,7 @@
6 6
   (apply #'format stream prompt args)
7 7
   (force-output stream)
8 8
   (read-line stream nil :done))
9
-;(trace prompt-for-line)
9
+;;(trace prompt-for-line)
10 10
 
11 11
 (defun prompt-for-lines (stream initial-prompt continuation-prompt &rest args)
12 12
   "Not finished ... see :description below"
... ...
@@ -13,6 +13,7 @@
13 13
 (sheeple:defproto =person= () (displayname emailaddress))
14 14
 (sheeple:defproto =issue= () (fields key id self))
15 15
 (sheeple:defproto =fields= () (summary description reporter creator assignee status comment))
16
+(sheeple:defproto =issues= () (issues))
16 17
 (sheeple:defmessage show (object &rest args))
17 18
 (sheeple:defmessage fields-labels (fields))
18 19
 (sheeple:defreply fields-labels ((fields =fields=))
... ...
@@ -21,6 +22,15 @@
21 22
 
22 23
 (sheeple:defproto =comment= () (self id author body))
23 24
 
25
+(sheeple:defmessage points (issue))
26
+(sheeple:defreply points ((issue =issue=))
27
+  (sheeple:property-value (fields issue)
28
+                          'customfield_10002))
29
+
30
+(sheeple:defreply sheeple:shared-init ((issues =issues=) &key)
31
+  (map nil (op (ensure-parent _ =issue=))
32
+       (issues issues)))
33
+
24 34
 (sheeple:defreply sheeple:shared-init :after ((comment =comment=) &key)
25 35
   (with-accessors ((author author)) comment
26 36
       (ensure-parent author =person= :err-if-nil nil)))
... ...
@@ -30,13 +40,14 @@
30 40
     (when fields
31 41
       (ensure-parent fields =fields=)
32 42
       (ensure-parent (status fields) =status=)
33
-      (ensure-parent (reporter fields) =person=)
34
-      (ensure-parent (creator fields) =person=)
43
+      (ensure-parent (reporter fields) =person= :err-if-nil nil)
44
+      (ensure-parent (creator fields) =person= :err-if-nil nil)
35 45
       (ensure-parent (assignee fields) =person= :err-if-nil nil)
36 46
       (sheeple:with-properties (comment) fields
37
-        (sheeple:with-properties (comments) comment
38
-          (map 'nil (lambda (comment) (ensure-parent comment =comment=))
39
-               comments))))))
47
+        (when comment
48
+          (sheeple:with-properties (comments) comment
49
+            (map 'nil (lambda (comment) (ensure-parent comment =comment=))
50
+                 comments)))))))
40 51
 
41 52
 (sheeple:defreply show ((person =person=) &rest args)
42 53
   (declare (ignore args))
... ...
@@ -63,16 +74,16 @@
63 74
             (pprint-newline :fill *standard-output*)))
64 75
         (pprint-newline :mandatory *standard-output*)))))
65 76
 
66
-(defun show-summary (summary)
67
-  (pprint-logical-block (*standard-output* (funcall (compose 'tokens 'trim-whitespace) summary))
77
+(defun show-summary (summary &optional (stream *standard-output*))
78
+  (pprint-logical-block (stream (funcall (compose 'tokens 'trim-whitespace) summary))
68 79
     (pprint-indent :block 8 *standard-output*)
69 80
     (pprint-exit-if-list-exhausted)
70
-    (format *standard-output* "~4tSummary: ")
81
+    (format stream "~4tSummary: ")
71 82
     (loop
72
-      (princ (pprint-pop))
83
+      (princ (pprint-pop) stream)
73 84
       (pprint-exit-if-list-exhausted)
74
-      (pprint-newline :fill *standard-output*)
75
-      (princ #\space))))
85
+      (pprint-newline :fill stream)
86
+      (princ #\space stream))))
76 87
 
77 88
 (sheeple:defreply show ((issue =issue=) &rest args)
78 89
   (declare (ignorable args))
... ...
@@ -149,20 +160,20 @@
149 160
 
150 161
 (defun json2sheeple (json &optional parent)
151 162
   (labels
152
-    ((handle-parsed (parsed-json)
153
-       (typecase parsed-json
154
-         (vector-of-objects (map 'vector #'handle-parsed parsed-json))
155
-         (hash-table
156
-           (let ((result (sheeple:object)))
157
-             (loop for json-prop being the hash-keys of parsed-json using (hash-value json-value)
158
-                   do (setf (sheeple:property-value result
159
-                                                    (intern (string-upcase json-prop) :jira-api))
160
-                            (typecase json-value
161
-                              (hash-table (handle-parsed json-value))
162
-                              (vector-of-objects (map 'vector #'handle-parsed json-value))
163
-                              (t json-value)))
164
-                   finally (return result))))
165
-         (t parsed-json))))
163
+      ((handle-parsed (parsed-json)
164
+         (typecase parsed-json
165
+           (vector-of-objects (map 'vector #'handle-parsed parsed-json))
166
+           (hash-table
167
+            (let ((result (sheeple:object)))
168
+              (loop for json-prop being the hash-keys of parsed-json using (hash-value json-value)
169
+                 do (setf (sheeple:property-value result
170
+                                                  (intern (string-upcase json-prop) :jira-api))
171
+                          (typecase json-value
172
+                            (hash-table (handle-parsed json-value))
173
+                            (vector-of-objects (map 'vector #'handle-parsed json-value))
174
+                            (t json-value)))
175
+                 finally (return result))))
176
+           (t parsed-json))))
166 177
     (let* ((yason:*parse-json-arrays-as-vectors* t)
167 178
            (result (handle-parsed (yason:parse json))))
168 179
       (when parent