Local Yarn Code

Check-in [f06db447]
Overview
Comment:Add RSS feed. Closes [5cca77420922765f]
Timelines: family | ancestors | trunk
Files: files | file ages | folders
SHA3-256: f06db447b86deb779d411b8ea37abea73d9284768deb7445e5b1c132a44bd631
User & Date: joel on 2019-08-19 21:36:57
Other Links: manifest | tags
Context
2019-08-19
21:36
Add RSS feed. Closes [5cca77420922765f] Leaf check-in: f06db447 user: joel tags: trunk
21:33
Add title-plain for notes; small refactor of note title generation check-in: 286673cf user: joel tags: trunk
Changes

Modified makefile from [c6be5061] to [f9d9c092].

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
60
61
62
63
64
65
66



67
68
69
70
71
72
73
series-html     := $(patsubst %.poly.pm, %.html, $(series-sources))

# ~~~ Rules ~~
#

# The order of these dependencies is important. They will be processed left to right.
web: vitreous.sqlite $(articles-html) $(series-html) 
web: index.html blog-pg1.html keyword-index.html web-extra/martin.css
web: ## Rebuild all web content (not PDFs)

# The file vitreous.sqlite is a cache of the rendered HTML and metadata. If it is older than any of
# its dependencies (or missing) all of the articles will be rebuilt. Its dependencies are also on
# the Pollen cache watchlist (see pollen.rkt)
vitreous.sqlite: $(core-files) $(html-deps) template.html.p
	raco pollen setup -p articles/
................................................................................

keyword-index.html: $(core-files) $(html-deps) $(articles-html) keyword-index.rkt
	racket -tm keyword-index.rkt
	# tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no $@ || true

web-extra/martin.css: web-extra/martin.css.pp
	raco pollen render $@




zap: ## Clear Pollen and Scribble cache, and remove all HTML output
	raco pollen reset
	rm -f *.html articles/*.html series/*.html vitreous.sqlite 

publish: check-env
publish: ## Sync all HTML and PDF stuff to the public web server (does not rebuild any files)







|







 







>
>
>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
series-html     := $(patsubst %.poly.pm, %.html, $(series-sources))

# ~~~ Rules ~~
#

# The order of these dependencies is important. They will be processed left to right.
web: vitreous.sqlite $(articles-html) $(series-html) 
web: index.html blog-pg1.html keyword-index.html web-extra/martin.css feed.xml
web: ## Rebuild all web content (not PDFs)

# The file vitreous.sqlite is a cache of the rendered HTML and metadata. If it is older than any of
# its dependencies (or missing) all of the articles will be rebuilt. Its dependencies are also on
# the Pollen cache watchlist (see pollen.rkt)
vitreous.sqlite: $(core-files) $(html-deps) template.html.p
	raco pollen setup -p articles/
................................................................................

keyword-index.html: $(core-files) $(html-deps) $(articles-html) keyword-index.rkt
	racket -tm keyword-index.rkt
	# tidy -quiet -modify -indent --wrap 100 --wrap-attributes no --tidy-mark no $@ || true

web-extra/martin.css: web-extra/martin.css.pp
	raco pollen render $@

feed.xml: vitreous.sqlite rss-feed.rkt
	racket -tm rss-feed.rkt

zap: ## Clear Pollen and Scribble cache, and remove all HTML output
	raco pollen reset
	rm -f *.html articles/*.html series/*.html vitreous.sqlite 

publish: check-env
publish: ## Sync all HTML and PDF stuff to the public web server (does not rebuild any files)

Added rss-feed.rkt version [e414aebe].



















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#lang racket/base

; SPDX-License-Identifier: BlueOak-1.0.0
; This file is licensed under the Blue Oak Model License 1.0.0.

;; Generates an Atom feed from the SQLite cache

(require txexpr
         racket/match
         racket/file
         racket/date
         racket/string
         db/base
         "dust.rkt"
         "crystalize.rkt")

(provide main)

(define feed-author default-authorname)
(define feed-author-email "joel@jdueck.net")
(define feed-title "The Local Yarn (Beta)")
(define feed-site-url "https://thelocalyarn.com")
(define feed-item-limit 50)

(define (as-cdata string)
  (cdata #f #f (format "<![CDATA[~a]]>" string))) ; cdata from xml package via txexpr

(define (email-encode str)
  (map char->integer (string->list str)))

;; Atom feeds require dates to be in RFC 3339 format
;; Our published/updated dates only give year-month-day. No need to bother about time zones or DST.
;; So, arbitrarily, everything happens at a quarter of noon UTC.
(define (ymd->rfc3339 datestr)
  (format "~aT11:45:00Z" datestr))

;; For the feed "updated" value, we do want a complete timestamp.
(define (current-rfc3339)
  ;; #f argument to seconds->date forces a UTC timestamp
  (define now (seconds->date (* 0.001 (current-inexact-milliseconds)) #f))
  (define timestamp
    (parameterize [(date-display-format 'iso-8601)]
      (date->string now #t)))
  (string-append timestamp "Z"))

;; Get the data out of the SQLite cache as vectors
(define (fetch-rows)
  (define fields '(pagenode title_plain published updated author doc_html))
  (define select #<<---
     SELECT `path`, `title`, `published`, `updated`, `author`, `entry-contents` FROM
       (SELECT `pagenode` AS `path`,
               `title_plain` AS `title`,
               `published`,
               `updated`,
               `author`,
               `doc_html` AS `entry-contents`
        FROM `articles`
        UNION
        SELECT `pagenode` || '#' || `note_id` AS `path`,
               `title_plain` AS `title`,
               `date` AS `published`,
               "" AS `updated`,
               `author`,
               `content_html` as `entry-contents`
        FROM `notes`)
        ORDER BY `published` DESC LIMIT ~a
---
    )
  (query-rows (sqltools:dbc) (format select feed-item-limit)))

(define (vector->rss-item vec)
  (match-define
    (vector path title published updated author contents) vec)
  (define entry-url (string-append feed-site-url web-root path))
  (define update-ts
    (cond [(non-empty-string? updated) updated]
          [else published]))
    
  `(entry (author    (name ,author))
          (published ,(ymd->rfc3339 published))
          (updated   ,(ymd->rfc3339 update-ts))
          (title     ,title)
          (link [[rel "alternate"] [href ,entry-url]])
          (id        ,entry-url)
          (summary [[type "html"]]
                   ,(as-cdata contents))))

(define (rss-feed)
  (define feed-xpr
    `(feed [[xml:lang "en-us"] [xmlns "http://www.w3.org/2005/Atom"]]
           (title ,feed-title)
           (link [[rel "self"] [href ,(string-append feed-site-url web-root "feed.xml")]])
           (generator [[uri "http://pollenpub.com/"]] "Pollen")
           (id ,(string-append feed-site-url web-root))
           (updated ,(current-rfc3339))
           (author
            (name ,feed-author)
            (email ,@(email-encode feed-author-email)))
           ,@(map vector->rss-item (fetch-rows))))
  (string-append "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 (xexpr->string feed-xpr)))

(define (main)
  (spell-of-summoning!) ; Turn on the cache DB connection
  (display-to-file (rss-feed) "feed.xml" #:mode 'text #:exists 'replace))