Browse code
chore: maintenance
fiddlerwoaroof authored on 21/06/2020 19:37:15
Showing 4 changed files
Showing 4 changed files
... | ... |
@@ -36,19 +36,19 @@ |
36 | 36 |
(items alimenta:items) |
37 | 37 |
(link alimenta:link) |
38 | 38 |
(source-type alimenta:source-type) |
39 |
- (title alimenta:title)) feed) |
|
40 |
- (yason:with-output (stream :indent t) |
|
41 |
- (yason:with-object () |
|
42 |
- (yason:encode-object-element "metadata" feed) |
|
43 |
- (yason:with-object-element ("items") |
|
44 |
- (yason:with-array () |
|
45 |
- (dolist (item item-storage-info) |
|
46 |
- (with-simple-restart (skip-item "Skip item ~s" (car item)) |
|
47 |
- ;; (format t "~&I Store Info: ~a~%~4t~a~%" (uiop:unix-namestring (cadr item)) root-dir) |
|
48 |
- (%encode-item root-dir item) |
|
49 |
- #+null |
|
50 |
- (yason:encode-array-element (uiop:unix-namestring (uiop:enough-pathname root-dir (cadr item)))) |
|
51 |
- ))))))) |
|
39 |
+ (title alimenta:title)) feed |
|
40 |
+ (yason:with-output (stream :indent t) |
|
41 |
+ (yason:with-object () |
|
42 |
+ (yason:encode-object-element "metadata" feed) |
|
43 |
+ (yason:with-object-element ("items") |
|
44 |
+ (yason:with-array () |
|
45 |
+ (dolist (item item-storage-info) |
|
46 |
+ (with-simple-restart (skip-item "Skip item ~s" (car item)) |
|
47 |
+ ;; (format t "~&I Store Info: ~a~%~4t~a~%" (uiop:unix-namestring (cadr item)) root-dir) |
|
48 |
+ (%encode-item root-dir item) |
|
49 |
+ #+null |
|
50 |
+ (yason:encode-array-element (uiop:unix-namestring (uiop:enough-pathname root-dir (cadr item)))) |
|
51 |
+ )))))))) |
|
52 | 52 |
|
53 | 53 |
(defmethod store ((items sequence) storage) |
54 | 54 |
(when (next-method-p) |
... | ... |
@@ -73,14 +73,15 @@ |
73 | 73 |
(title alimenta:title)) feed |
74 | 74 |
; We wrap all errors with our own condition |
75 | 75 |
(handler-bind ((error (lambda (c) (error 'feed-error :feed feed :condition c)))) |
76 |
- (values (prog1-let ((feed-title title) |
|
77 |
- (feed-store (get-feed-store-name feed directory))) |
|
78 |
- (ensure-directories-exist feed-store) |
|
79 |
- (with-open-file (index (merge-pathnames "index.json" feed-store) :direction :output) |
|
80 |
- (%encode-feed-as-json feed |
|
81 |
- (store (copy-seq items) feed-store) |
|
82 |
- feed-store |
|
83 |
- index))) |
|
76 |
+ (values (multiple-value-list |
|
77 |
+ (prog1-let ((feed-title title) |
|
78 |
+ (feed-store (get-feed-store-name feed directory))) |
|
79 |
+ (ensure-directories-exist feed-store) |
|
80 |
+ (with-open-file (index (merge-pathnames "index.json" feed-store) :direction :output) |
|
81 |
+ (%encode-feed-as-json feed |
|
82 |
+ (store (copy-seq items) feed-store) |
|
83 |
+ feed-store |
|
84 |
+ index)))) |
|
84 | 85 |
feed-link))))) |
85 | 86 |
|
86 | 87 |
(defmethod store ((feed alimenta:feed) (stream stream)) |
... | ... |
@@ -104,14 +105,16 @@ |
104 | 105 |
(let ((id (get-id item))) |
105 | 106 |
(merge-pathnames (make-pathname :name id :version nil :type "json") directory)))) |
106 | 107 |
|
107 |
- (prog1-let ((item-title (alimenta:title item)) |
|
108 |
- (fn (get-item-store-name item directory))) |
|
109 |
- (with-open-file (item-f fn :direction :output) |
|
110 |
- (yason:encode item item-f))))) |
|
108 |
+ (multiple-value-list |
|
109 |
+ (prog1-let ((item-title (alimenta:title item)) |
|
110 |
+ (fn (get-item-store-name item directory))) |
|
111 |
+ (with-open-file (item-f fn :direction :output) |
|
112 |
+ (yason:encode item item-f)))))) |
|
111 | 113 |
|
112 | 114 |
(defmethod store ((item alimenta:item) (stream stream)) |
113 | 115 |
(yason:with-output (stream :indent t) |
114 |
- (yason:encode-slots item)) |
|
116 |
+ (yason:with-object () |
|
117 |
+ (yason:encode-slots item))) |
|
115 | 118 |
(list (alimenta:title item) |
116 | 119 |
stream)) |
117 | 120 |
|
... | ... |
@@ -133,9 +136,15 @@ |
133 | 136 |
(let ((id (get-id item))) |
134 | 137 |
(make-pathname :name id :version nil :type "json"))) |
135 | 138 |
|
136 |
-(defclass feed-stream-provider (stream-provider:file-provider) |
|
139 |
+(defclass feed-stream-item-provider () |
|
137 | 140 |
((%item-providers :accessor item-providers :initform (make-hash-table :test 'equal)))) |
138 | 141 |
|
142 |
+(defclass feed-stream-provider (stream-provider:file-provider feed-stream-item-provider) |
|
143 |
+ ()) |
|
144 |
+ |
|
145 |
+(defclass feed-stream-string-provider (stream-provider:string-provider feed-stream-item-provider) |
|
146 |
+ ()) |
|
147 |
+ |
|
139 | 148 |
(defmethod stream-provider:get-nested-provider ((provider stream-provider:stream-provider) (streamable alimenta:feed)) |
140 | 149 |
(with (items-root (uiop:merge-pathnames* (uiop:pathname-directory-pathname (stream-provider:stream-key provider streamable)) |
141 | 150 |
(stream-provider:root provider))) |
... | ... |
@@ -173,10 +182,11 @@ |
173 | 182 |
(let* ((item-provider (stream-provider:get-nested-provider stream-provider feed)) |
174 | 183 |
(item-storage-info (map-coalesce (op (store _ item-provider)) |
175 | 184 |
items))) |
176 |
- (yason:with-output (s :indent t) |
|
185 |
+ (let ((yason::*json-output* |
|
186 |
+ (make-instance 'yason::json-output-stream |
|
187 |
+ :output-stream s |
|
188 |
+ :indent t))) |
|
177 | 189 |
(with-collection (item "items" item-storage-info "metadata" feed) |
178 | 190 |
(destructuring-bind (title path) item |
179 | 191 |
(yason:with-object () |
180 |
- (yason:encode-object-elements |
|
181 |
- "title" title |
|
182 |
- "path" path))))))))) |
|
192 |
+ (yason:encode-object-elements "title" title "path" path))))))))) |
... | ... |
@@ -8,12 +8,14 @@ |
8 | 8 |
|
9 | 9 |
(defun get-store-directory-name (timestamp) |
10 | 10 |
(flet ((make-dirname (timestamp) |
11 |
- (-> (local-time:format-timestring nil (local-time:timestamp-minimize-part timestamp :sec) |
|
12 |
- :format +dirname-format+) |
|
13 |
- (merge-pathnames *feed-base*)))) |
|
14 |
- (-> (prog1-let ((result (make-dirname timestamp))) |
|
15 |
- (ensure-directories-exist result)) |
|
16 |
- (car)))) |
|
11 |
+ (merge-pathnames |
|
12 |
+ (local-time:format-timestring nil |
|
13 |
+ (local-time:timestamp-minimize-part timestamp |
|
14 |
+ :sec) |
|
15 |
+ :format +dirname-format+) |
|
16 |
+ *feed-base*))) |
|
17 |
+ (values (prog1-let ((result (make-dirname timestamp))) |
|
18 |
+ (ensure-directories-exist result))))) |
|
17 | 19 |
|
18 | 20 |
(defun test-feed-list () |
19 | 21 |
(values '("http://feeds.feedburner.com/GamasutraFeatureArticles/" |
... | ... |
@@ -37,30 +39,39 @@ |
37 | 39 |
(ubiquitous:value :feeds) |
38 | 40 |
:test #'equalp)) |
39 | 41 |
|
42 |
+(defmacro lambda* ((&rest args) &body body) |
|
43 |
+ (let ((rest-arg (gensym "REST"))) |
|
44 |
+ `(lambda (,@args &rest ,rest-arg) |
|
45 |
+ (declare (ignore ,rest-arg)) |
|
46 |
+ ,@body))) |
|
47 |
+ |
|
40 | 48 |
(defun safe-pull-feed (feed-url &aux (pop-times 0)) |
41 | 49 |
"Handles date parsing errors in the feed: chronicity won't parse |
42 | 50 |
certain date formats, this catches the error and modifies the |
43 | 51 |
format to something chronicity can handle." |
44 |
- (flet ((pop-50-tokens (c) |
|
45 |
- (declare (ignore c)) |
|
46 |
- (when (find-restart 'alimenta:pop-token) |
|
47 |
- (if (< pop-times 50) |
|
48 |
- (progn (incf pop-times) |
|
49 |
- (format t "~&Processing error, trying to pop a token (popped ~d times)~%" |
|
50 |
- pop-times) |
|
51 |
- (alimenta:pop-token)) |
|
52 |
- (continue))))) |
|
53 |
- (handler-bind ((warning #'muffle-warning) |
|
54 |
- (error #'pop-50-tokens)) |
|
55 |
- (prog1-bind (feed (alimenta.pull-feed:pull-feed feed-url)) |
|
56 |
- ;; Why am I decf-ing here? |
|
57 |
- (alimenta:transform feed |
|
58 |
- (fw.lu:glambda (entity) |
|
59 |
- (:method (entity)) |
|
60 |
- (:method ((entity alimenta:item)) |
|
61 |
- (setf (alimenta:content entity) |
|
62 |
- (html-sanitizer:sanitize (alimenta:content entity)))))) |
|
63 |
- (decf pop-times))))) |
|
52 |
+ (handler-bind ((warning #'muffle-warning) |
|
53 |
+ (error (lambda* (c) |
|
54 |
+ (when (find-restart 'alimenta:pop-token c) |
|
55 |
+ (cond |
|
56 |
+ ((< pop-times 50) |
|
57 |
+ (incf pop-times) |
|
58 |
+ (format t |
|
59 |
+ "~&Processing error, trying to pop a token (popped ~d times)~%" |
|
60 |
+ pop-times) |
|
61 |
+ (alimenta:pop-token)) |
|
62 |
+ (t |
|
63 |
+ (continue))))))) |
|
64 |
+ (prog1-bind (feed (alimenta.pull-feed:pull-feed feed-url)) |
|
65 |
+ ;; Why am I decf-ing here? |
|
66 |
+ (alimenta:transform feed |
|
67 |
+ (fw.lu:glambda (entity) |
|
68 |
+ (:method (entity)) |
|
69 |
+ (:method ((entity alimenta:item)) |
|
70 |
+ (let ((v (alimenta:content entity))) |
|
71 |
+ (when v |
|
72 |
+ (setf (alimenta:content entity) |
|
73 |
+ (html-sanitizer:sanitize v))))))) |
|
74 |
+ (decf pop-times)))) |
|
64 | 75 |
|
65 | 76 |
(defmacro with-progress-message ((stream before after &optional (error-msg " ERROR~%~4t~a~%")) &body body) |
66 | 77 |
(once-only (before after stream) |
... | ... |
@@ -115,10 +126,10 @@ |
115 | 126 |
(merge-pathnames path |
116 | 127 |
(stream-provider:root stream-provider))))) |
117 | 128 |
(handler-bind ((cl+ssl:ssl-error-verify |
118 |
- (lambda (c) |
|
119 |
- (declare (ignore c)) |
|
120 |
- (format *error-output* "~&SSL Error while pulling ~a~%" |
|
121 |
- feed-url)))) |
|
129 |
+ (lambda (c) |
|
130 |
+ (declare (ignore c)) |
|
131 |
+ (format *error-output* "~&SSL Error while pulling ~a~%" |
|
132 |
+ feed-url)))) |
|
122 | 133 |
(with-simple-restart (skip-feed "Stop processing for ~a" feed-url) |
123 | 134 |
(let* ((feed (with-retry ("Pull feed again.") |
124 | 135 |
(normalize-feed feed-url (log-pull t))))) |
... | ... |
@@ -139,7 +150,7 @@ |
139 | 150 |
|
140 | 151 |
(defun archive-feeds-nondeterm () |
141 | 152 |
(let* ((pull-time (local-time:now)) |
142 |
- (pull-directory (get-store-directory-name pull-time)) |
|
153 |
+ (pull-directory (get-store-directory-name pull-time)) |
|
143 | 154 |
(index-path (merge-pathnames "index.json" pull-directory)) |
144 | 155 |
(feed-stream-provider (make-instance 'alimenta.feed-archive.encoders:feed-stream-provider |
145 | 156 |
:if-exists :error |
... | ... |
@@ -157,34 +168,44 @@ |
157 | 168 |
(alimenta:feed-type c) |
158 | 169 |
(alimenta:feed-link c)) |
159 | 170 |
(funcall restart)) |
160 |
- (fix-pathname-or-skip (c &key (restart 'skip-feed) (wrapped-condition nil wc-p)) |
|
171 |
+ (fix-pathname-or-skip (c &key |
|
172 |
+ (restart 'skip-feed) |
|
173 |
+ (wrapped-condition nil wc-p)) |
|
161 | 174 |
(typecase (or wrapped-condition c) |
162 | 175 |
(alimenta:feed-type-unsupported (feed-type-unsupported c)) |
163 |
- (otherwise |
|
176 |
+ (t |
|
164 | 177 |
(if (find-restart 'fix-pathname) |
165 | 178 |
(fix-pathname) |
166 |
- (progn (unless (eq restart 'continue) |
|
167 |
- (format t "~&Skipping a feed... ~s~%" |
|
168 |
- (if wc-p |
|
169 |
- (alimenta.feed-archive.encoders:the-feed c) |
|
170 |
- "Unknown"))) |
|
171 |
- (funcall restart))))))) |
|
179 |
+ (if (find-restart 'alimenta.pull-feed:skip-feed) |
|
180 |
+ (alimenta.pull-feed:skip-feed c) |
|
181 |
+ (progn |
|
182 |
+ (unless (eq restart 'continue) |
|
183 |
+ (format t "~&Skipping a feed... ~s~%" |
|
184 |
+ (if wc-p |
|
185 |
+ (alimenta.feed-archive.encoders:the-feed c) |
|
186 |
+ "Unknown"))) |
|
187 |
+ (funcall restart)))))))) |
|
172 | 188 |
|
173 | 189 |
(let ((error-count 0)) |
174 |
- (handler-bind ((alimenta.feed-archive.encoders:feed-error |
|
175 |
- (op (fix-pathname-or-skip _1 :wrapped-condition (alimenta.feed-archive.encoders:the-condition _1)))) |
|
176 |
- (alimenta:feed-type-unsupported #'feed-type-unsupported) |
|
177 |
- ((or usocket:timeout-error usocket:ns-error cl+ssl:ssl-error-verify) |
|
178 |
- (op (alimenta.pull-feed:skip-feed _))) |
|
179 |
- |
|
180 |
- (error |
|
181 |
- (op |
|
182 |
- (format t "~&Error signaled, ~a (count ~d)" _1 error-count) |
|
183 |
- (incf error-count) |
|
184 |
- (unless (< error-count 15) |
|
185 |
- (format t " continuing~%") |
|
186 |
- (fix-pathname-or-skip _1 :restart 'continue))))) |
|
187 |
- (multiple-value-bind (*feeds* *feed-base*) (funcall feed-list-initializer) |
|
190 |
+ (handler-bind |
|
191 |
+ ((alimenta.feed-archive.encoders:feed-error |
|
192 |
+ (op (fix-pathname-or-skip |
|
193 |
+ _1 :wrapped-condition |
|
194 |
+ (alimenta.feed-archive.encoders:the-condition _1)))) |
|
195 |
+ (alimenta:feed-type-unsupported #'feed-type-unsupported) |
|
196 |
+ ((or usocket:timeout-error usocket:ns-error cl+ssl:ssl-error-verify) |
|
197 |
+ (op (alimenta.pull-feed:skip-feed _))) |
|
198 |
+ |
|
199 |
+ (error |
|
200 |
+ (op |
|
201 |
+ (format t "~&Error signaled, ~a (count ~d)" |
|
202 |
+ _1 error-count) |
|
203 |
+ (incf error-count) |
|
204 |
+ (unless (< error-count 15) |
|
205 |
+ (format t " continuing~%") |
|
206 |
+ (fix-pathname-or-skip _1 :restart 'continue))))) |
|
207 |
+ (multiple-value-bind (*feeds* *feed-base*) |
|
208 |
+ (funcall feed-list-initializer) |
|
188 | 209 |
(alimenta.pull-feed:with-user-agent ("Feed Archiver v0.1b") |
189 | 210 |
(archive-feeds-nondeterm))))))) |
190 | 211 |
|
... | ... |
@@ -208,11 +229,11 @@ |
208 | 229 |
(alexandria:hash-table-values ht2))))) |
209 | 230 |
|
210 | 231 |
(deftest feed-index () |
211 |
- (should be hash-table= |
|
212 |
- (yason:parse |
|
213 |
- (with-output-to-string (s) |
|
214 |
- (feed-index s (local-time:encode-timestamp 0 0 0 0 1 1 1) '())) |
|
215 |
- :object-as :hash-table :json-arrays-as-vectors nil) |
|
216 |
- (alexandria:alist-hash-table |
|
217 |
- '(("pull-time" . "0001-01-01T00:00:00.000000-08:00") |
|
218 |
- ("feeds" . ()))))) |
|
232 |
+ (should be hash-table= |
|
233 |
+ (yason:parse |
|
234 |
+ (with-output-to-string (s) |
|
235 |
+ (feed-index s (local-time:encode-timestamp 0 0 0 0 1 1 1) '())) |
|
236 |
+ :object-as :hash-table :json-arrays-as-vectors nil) |
|
237 |
+ (alexandria:alist-hash-table |
|
238 |
+ '(("pull-time" . "0001-01-01T00:00:00.000000-08:00") |
|
239 |
+ ("feeds" . ()))))) |
... | ... |
@@ -12,17 +12,18 @@ |
12 | 12 |
|
13 | 13 |
(defun make-feed-index (pull-time references) |
14 | 14 |
(make-instance 'feed-index |
15 |
- :pull-time pull-time |
|
16 |
- :references (copy-seq references))) |
|
15 |
+ :pull-time pull-time |
|
16 |
+ :references (copy-seq references))) |
|
17 | 17 |
|
18 | 18 |
(defun make-feed-reference (url &rest feed-data &key title path) |
19 | 19 |
(declare (ignore title path)) |
20 | 20 |
(apply #'make-instance 'feed-reference |
21 |
- :url url feed-data)) |
|
21 |
+ :url url |
|
22 |
+ feed-data)) |
|
22 | 23 |
|
23 | 24 |
(defmethod yason:encode-slots progn ((object feed-reference)) |
24 | 25 |
(let ((title (title object)) |
25 |
- (path (path object))) |
|
26 |
+ (path (path object))) |
|
26 | 27 |
(yason:encode-object-element "url" (url object)) |
27 | 28 |
(when title |
28 | 29 |
(yason:encode-object-element "title" title)) |
... | ... |
@@ -34,4 +35,4 @@ |
34 | 35 |
(yason:encode-object-element "pull-time" (local-time:format-timestring nil pull-time)) |
35 | 36 |
(yason:with-object-element ("feeds") |
36 | 37 |
(yason:with-array () |
37 |
- (mapcar 'yason:encode-object references))))) |
|
38 |
+ (mapcar 'yason:encode-object references))))) |
... | ... |
@@ -43,8 +43,7 @@ |
43 | 43 |
(concatenate 'string |
44 | 44 |
(local-time:format-timestring nil (alimenta:date item)) |
45 | 45 |
"-" |
46 |
- (sha256-string (alimenta:id item)) |
|
47 |
- #+nil ".json")) |
|
46 |
+ (sha256-string (alimenta:id item)))) |
|
48 | 47 |
|
49 | 48 |
(defun older-than-a-month (date) |
50 | 49 |
(let ((month-ago (local-time:timestamp- (local-time:now) |