Skip to content

Commit 28f496c

Browse files
committed
Event publishing workflow
1 parent f706bd6 commit 28f496c

File tree

3 files changed

+178
-0
lines changed

3 files changed

+178
-0
lines changed

.github/workflows/publish-events.yml

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Daily Event Publishing
2+
3+
on:
4+
workflow_dispatch:
5+
# schedule:
6+
# - cron: '0 0 * * *' # Daily at midnight UTC
7+
8+
jobs:
9+
daily-backup:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/setup-java@v4
14+
with:
15+
distribution: 'temurin'
16+
java-version: '8'
17+
18+
- name: Set up Clojure
19+
uses: DeLaGuardo/[email protected]
20+
with:
21+
cli: 'latest'
22+
23+
- name: Install CLI
24+
run: |
25+
curl -L -O https://github.com/clojure/brew-install/releases/latest/download/posix-install.sh
26+
chmod +x posix-install.sh
27+
sudo ./posix-install.sh
28+
clojure
29+
30+
- name: Check out the repository
31+
uses: actions/checkout@v4
32+
33+
- name: Run Event Script
34+
run: |
35+
cd script/eventgen
36+
clojure -X parse/parse-feed :dir '"../../content/events"'
37+
38+
- name: Commit and push event page
39+
run: |
40+
cd content
41+
git config --global user.name clojure-build
42+
git config --global user.email "[email protected]"
43+
git add content/events
44+
git commit -m 'update events'
45+
git push

script/eventgen/deps.edn

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{:paths ["src"]
2+
:deps
3+
{org.mnode.ical4j/ical4j {:mvn/version "4.0.0-beta5"}
4+
org.slf4j/slf4j-nop {:mvn/version "1.7.36"}}}

script/eventgen/src/parse.clj

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
(ns parse
2+
(:require
3+
[clojure.java.io :as jio]
4+
[clojure.string :as str])
5+
(:import
6+
[java.time LocalDate]
7+
[java.io StringReader]
8+
[net.fortuna.ical4j.model PropertyListAccessor]
9+
[net.fortuna.ical4j.data CalendarBuilder]))
10+
11+
(defn coerce-date
12+
[s]
13+
(try
14+
(str (subs s 0 4) "-" (subs s 4 6) "-" (subs s 6 8))
15+
(catch Throwable _ s)))
16+
17+
(defn extract-props
18+
[^PropertyListAccessor component]
19+
(loop [[p & rp] (.getAll (.getPropertyList component))
20+
m {}]
21+
(if p
22+
(let [prop-name (-> p .getName str/lower-case keyword)
23+
prop-val (cond-> (.getValue p)
24+
(#{:dtstart :dtend} prop-name) (coerce-date))]
25+
(recur rp (cond-> m (not (str/blank? prop-val)) (assoc prop-name prop-val))))
26+
m)))
27+
28+
(defn trim-punc-r
29+
[s]
30+
(let [last (dec (.length s))]
31+
(if (contains? #{\space \tab \newline \formfeed \backspace \return \: \( \- \& \| \,}
32+
(.charAt s last))
33+
(trim-punc-r (subs s 0 last))
34+
s)))
35+
36+
(defn title
37+
[summary limit]
38+
(if (< (count summary) limit)
39+
(trim-punc-r summary)
40+
(let [prefixes (->> [(second (re-matches #"(.[^:]+):.*" summary)) ;; . to skip leading colon
41+
(second (re-matches #"(.[^(]+)\(.*" summary)) ;; . to skip leading (
42+
(second (re-matches #"(.+)\s-.*" summary)) ;; \s as - is commonly in kebab case names
43+
(second (re-matches #"(.+)&ndash.*" summary))
44+
(second (re-matches #"(.+)&mdash.*" summary))]
45+
;(#(do (println %) %))
46+
(remove nil?)
47+
(remove #(>= (count %) limit))
48+
(remove #(< (count %) 15))
49+
(sort-by count))]
50+
(if (pos? (count prefixes))
51+
(trim-punc-r (last prefixes))
52+
(let [chop (subs summary 0 40)
53+
ri (str/last-index-of chop " ")]
54+
(trim-punc-r (subs summary 0 ri)))))))
55+
56+
(defn add-title
57+
[event]
58+
(if-let [summary (:summary event)]
59+
(assoc event :title (title summary 40))
60+
event))
61+
62+
(defn adoc-file
63+
[event]
64+
(let [title (:title event)
65+
year (subs (:dtstart event) 0 4)]
66+
(str year "/"
67+
(-> title
68+
str/lower-case
69+
(str/replace #"[^a-z0-9\-]" (constantly "-"))
70+
(str/replace #"[-]{2,}+" "-")
71+
trim-punc-r)
72+
"-"
73+
(hash (:uid event))
74+
".adoc")))
75+
76+
(defn write-event
77+
[dir {:keys [dtstart dtend title location summary description url] :as event}]
78+
(let [f (jio/file dir (adoc-file event))
79+
contents
80+
(str "= " title "\n"
81+
dtstart "\n"
82+
":jbake-type: event\n"
83+
":jbake-edition: \n" ;; not using
84+
":jbake-link: " url "\n"
85+
":jbake-location: " (or location "unknown") "\n"
86+
":jbake-start: " dtstart "\n"
87+
":jbake-end: " dtend "\n"
88+
"\n"
89+
"== " summary "\n\n"
90+
(str/join "\n" (map #(str % " +") (str/split description #"\r?\n|\r"))) "\n\n")]
91+
(jio/make-parents f)
92+
(spit f contents)))
93+
94+
(defn parse-feed
95+
[{:keys [dir]}]
96+
(let [ics (slurp "https://www.clojurians-zulip.org/feeds/events.ics")
97+
builder (CalendarBuilder.)
98+
cal (.build builder (StringReader. ics))
99+
comps (.getComponents cal)
100+
year (.getYear (LocalDate/now))
101+
events (->> comps
102+
(map extract-props)
103+
(filter #(<= year (parse-long (subs (:dtstart %) 0 4))))
104+
(map add-title))]
105+
(run! #(write-event dir %) events)))
106+
107+
(comment
108+
(def ics (slurp "https://www.clojurians-zulip.org/feeds/events.ics"))
109+
(def builder (CalendarBuilder.))
110+
(def cal (.build builder (StringReader. ics)))
111+
(def comps (.getComponents cal))
112+
(count comps)
113+
(take 50 (map class comps))
114+
(class (first comps))
115+
(def c (first comps))
116+
(class (second (.getProperties c)))
117+
(count (.getComponentList c))
118+
(.getProperty c "dtstart")
119+
(extract-props c)
120+
(set! *print-length* 100)
121+
(let [ds (take 100 (map add-title (map extract-props comps)))]
122+
(run! #(println (adoc-file %)) ds)
123+
#_(run! #(println (:dtstart %) (:dtend %)) ds)
124+
#_(run! #(println (title (:summary %) 40) "\t" %) ds))
125+
126+
127+
(parse-feed {:dir "/Users/alex.miller/code/clojure-site/content/events"})
128+
129+
)

0 commit comments

Comments
 (0)