Skip to content

Instantly share code, notes, and snippets.

@jkrumbiegel
Last active July 21, 2025 14:30
Show Gist options
  • Save jkrumbiegel/400d42014bc40c55e2d301f91c0be2ad to your computer and use it in GitHub Desktop.
Save jkrumbiegel/400d42014bc40c55e2d301f91c0be2ad to your computer and use it in GitHub Desktop.
juliacon pretalx custom pdf schedule
using JSON
using Downloads
using Typst_jll
using Dates
url = "https://pretalx.com/juliacon-2025/schedule/export/schedule.json"
json = JSON.parsefile(Downloads.download(url))
days = json["schedule"]["conference"]["days"][3:5]
function tesc(io::IO, s::AbstractString)
escapable_special_chars = raw"\$#*_<@`"
a = Iterators.Stateful(s)
for c in a
if c in escapable_special_chars
print(io, '\\', c)
else
print(io, c)
end
end
end
function tesc(s::AbstractString)
return sprint(tesc, s, sizehint=lastindex(s))
end
colwidth_pt = 120
function roomsorter(room)
(!occursin("Main", room), !occursin("David", room), room)
end
open("typst.typ", "w") do io
println(io, """
#set page(width: auto, height: auto, margin: 10pt)
#set text(font: "SF Hello")
""")
for (iday, day) in enumerate(days)
println(io, "= ", Dates.format(Date(day["date"]), "E, d U yyyy"), "\n")
time_edges = Iterators.flatmap(values(day["rooms"])) do talks
Iterators.flatmap(talks) do talk
[Time(talk["start"]), Time(talk["start"]) + (Time(talk["duration"]) - Time(0))]
end
end |> Set |> collect |> sort |> enumerate .|> reverse |> Dict
roomnames = sort(collect(keys(day["rooms"])), by = roomsorter)
roomgaps = first.(roomnames, 4)[1:end-1] .!= first.(roomnames, 4)[2:end]
rooms = Dict(reverse.(enumerate(roomnames)))
cells = Iterators.flatmap(pairs(day["rooms"])) do (room, talks)
map(talks) do talk
start = Time(talk["start"])
stop = Time(talk["start"]) + (Time(talk["duration"]) - Time(0))
row = time_edges[start]
rowspan = time_edges[stop] - row
(row, rooms[room], rowspan, talk)
end
end |> collect |> sort
println(io, "#table(")
println(io, " stroke: none,")
println(io, " row-gutter: 2pt,")
println(io, " column-gutter: (2pt,) + ($(join([r ? "20pt" : "2pt" for r in roomgaps], ", "))),")
println(io, " columns: (auto,) + $(length(rooms)) * ($(colwidth_pt)pt,),")
println(io, " rows: $(length(time_edges)) + 1,")
for (room, col) in rooms
println(io, " table.cell(x: $col, y: 0, fill: luma(240))[*$room*],")
end
for (time, row) in time_edges
println(io, " table.cell(x: 0, y: $(row), fill: luma(240))[*$(Dates.format(time, "HH:MM"))*],")
end
for (row, col, rowspan, talk) in cells
start = Time(talk["start"])
stop = Time(talk["start"]) + (Time(talk["duration"]) - Time(0))
time = "#text(size: 4pt, [$(Dates.format(start, "HH:MM")) - $(Dates.format(stop, "HH:MM"))])"
title = tesc(replace(talk["title"], r"(?<=[a-z])(?=[A-Z])" => "\u200B")) # allow breaks in package names in titles
names = join([tesc(p["name"]) for p in talk["persons"]], ", ", " & ")
abstract = tesc(talk["abstract"])
room = "#text([$(tesc(talk["room"]))], size: 4pt)"
abstract_small = "#text([$abstract], size: 4pt)"
content = "$time \n\n $room \n\n *$title*\\ $names \n\n $abstract_small"
println(io, " table.cell(fill: color.oklch(93%, 30%, $(360 * col / length(rooms))deg), y: $(row), x: $(col), rowspan: $rowspan)[$content],")
end
println(io, ")")
iday < length(days) && println(io, "#pagebreak()")
end
end
Typst_jll.typst() do bin
run(`$bin compile typst.typ`)
end
using JSON
using Downloads
using Typst_jll
using Dates
url = "https://pretalx.com/juliacon-2025/schedule/export/schedule.json"
json = JSON.parsefile(Downloads.download(url))
days = json["schedule"]["conference"]["days"][3:5]
function tesc(io::IO, s::AbstractString)
escapable_special_chars = raw"\$#*_<@`"
a = Iterators.Stateful(s)
for c in a
if c in escapable_special_chars
print(io, '\\', c)
else
print(io, c)
end
end
end
function tesc(s::AbstractString)
return sprint(tesc, s, sizehint=lastindex(s))
end
colwidth_pt = 120
function roomsorter(room)
(!occursin("Main", room), !occursin("David", room), room)
end
calendar_entries = [m.match for m in eachmatch(r"^BEGIN:VEVENT[\s\S]*?END:VEVENT"m, read("juliacon-2025-favs.ics", String))]
favorites_codes = [match(r"UID:pretalx-juliacon-2025-(.*?)@pretalx.com", e)[1] for e in calendar_entries]
open("typst.typ", "w") do io
println(io, """
#set page(width: auto, height: auto, margin: 10pt)
#set text(font: "Avenir")
""")
for (iday, day) in enumerate(days)
println(io, "= ", Dates.format(Date(day["date"]), "E, d U yyyy"), "\n")
time_edges = Iterators.flatmap(values(day["rooms"])) do talks
Iterators.flatmap(talks) do talk
[Time(talk["start"]), Time(talk["start"]) + (Time(talk["duration"]) - Time(0))]
end
end |> Set |> collect |> sort |> enumerate .|> reverse |> Dict
roomnames = sort(collect(keys(day["rooms"])), by = roomsorter)
roomgaps = first.(roomnames, 4)[1:end-1] .!= first.(roomnames, 4)[2:end]
rooms = Dict(reverse.(enumerate(roomnames)))
cells = Iterators.flatmap(pairs(day["rooms"])) do (room, talks)
map(talks) do talk
start = Time(talk["start"])
stop = Time(talk["start"]) + (Time(talk["duration"]) - Time(0))
row = time_edges[start]
rowspan = time_edges[stop] - row
(row, rooms[room], rowspan, talk)
end
end |> collect |> sort
println(io, "#table(")
println(io, " stroke: none,")
println(io, " row-gutter: 2pt,")
println(io, " column-gutter: (2pt,) + ($(join([r ? "20pt" : "2pt" for r in roomgaps], ", "))),")
println(io, " columns: (auto,) + $(length(rooms)) * ($(colwidth_pt)pt,),")
println(io, " rows: $(length(time_edges)) + 1,")
for (room, col) in rooms
println(io, " table.cell(x: $col, y: 0, fill: luma(240))[*$room*],")
end
for (time, row) in time_edges
println(io, " table.cell(x: 0, y: $(row), fill: luma(240))[*$(Dates.format(time, "HH:MM"))*],")
end
for (row, col, rowspan, talk) in cells
is_favorite = talk["code"] in favorites_codes
start = Time(talk["start"])
stop = Time(talk["start"]) + (Time(talk["duration"]) - Time(0))
time = "#text(size: 4pt, [$(Dates.format(start, "HH:MM")) - $(Dates.format(stop, "HH:MM"))])"
title = tesc(replace(talk["title"], r"(?<=[a-z])(?=[A-Z])" => "\u200B")) # allow breaks in package names in titles
names = join([tesc(p["name"]) for p in talk["persons"]], ", ", " & ")
abstract = tesc(talk["abstract"])
room = "#text([$(tesc(talk["room"]))], size: 4pt)"
abstract_small = "#text([$abstract], size: 4pt)"
content = "$time \n\n $room \n\n *$title*\\ $names \n\n $abstract_small"
fill = is_favorite ? "yellow" : "color.oklch(93%, 30%, $(360 * col / length(rooms))deg)"
println(io, " table.cell(fill: $fill, y: $(row), x: $(col), rowspan: $rowspan)[$content],")
end
println(io, ")")
iday < length(days) && println(io, "#pagebreak()")
end
end
Typst_jll.typst() do bin
run(`$bin compile typst.typ`)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment