(require 'org)
(require 'cl-lib)
(defun event/parse-sessions ()
"Parse event sessions from the current Org buffer."
(org-element-map (org-element-parse-buffer) 'headline
(lambda (headline)
(let ((title (org-element-property :raw-value headline))
(properties (org-element-property :properties headline)))
(when (and (org-element-property :LOCATION properties)
(org-element-property :SCHEDULED properties))
(list :title title
:time (org-element-property :SCHEDULED properties)
:location (org-element-property :LOCATION properties)
:duration (org-element-property :DURATION properties)
:speaker (or (org-element-property :SPEAKER properties)
(org-element-property :ORGANIZER properties))
:id (org-element-property :ID properties)))))))
(defun event/parse-time (time-string)
"Parse TIME-STRING and return a cons of (hours . minutes)."
(when (string-match "\\([0-9]+\\):\\([0-9]+\\)" time-string)
(cons (string-to-number (match-string 1 time-string))
(string-to-number (match-string 2 time-string)))))
(defun event/format-time (time)
"Format TIME as HH:MM."
(format "%02d:%02d" (car time) (cdr time)))
(defun event/get-tracks (sessions)
"Get unique tracks from SESSIONS."
(delete-dups
(mapcar (lambda (session) (plist-get session :location))
sessions)))
(defun event/sort-sessions (sessions)
"Sort SESSIONS by time."
(sort sessions
(lambda (a b)
(time-less-p
(org-time-string-to-time (plist-get a :time))
(org-time-string-to-time (plist-get b :time))))))
(defun event/generate-table-by-track (sessions)
"Generate a table of SESSIONS organized by track."
(let* ((tracks (event/get-tracks sessions))
(sorted-sessions (event/sort-sessions sessions))
(table (list (cons "Time" tracks))))
(dolist (session sorted-sessions)
(let* ((time (event/parse-time (plist-get session :time)))
(formatted-time (event/format-time time))
(track (plist-get session :location))
(title (plist-get session :title))
(speaker (plist-get session :speaker))
(row-index (cl-position formatted-time table
:key #'car :test #'string=))
(cell-content (format "%s\n%s" title (or speaker ""))))
(if row-index
(setf (nth (1+ (cl-position track tracks :test #'string=))
(nth row-index table))
cell-content)
(let ((new-row (make-list (1+ (length tracks)) "")))
(setf (car new-row) formatted-time)
(setf (nth (1+ (cl-position track tracks :test #'string=)) new-row)
cell-content)
(setq table (append table (list new-row)))))))
(concat "| "
(mapconcat #'identity
(mapcar (lambda (row)
(mapconcat #'identity row " | "))
table)
" |\n| ")
" |")))
(defun event/write-table-to-file (table-string filename)
"Write TABLE-STRING to FILENAME."
(with-temp-file filename
(insert "#+TITLE: Heart of Clojure 2024 Schedule\n\n")
(insert table-string)))
(defun event/generate-schedule-table ()
"Generate a schedule table from the current Org buffer and save it to a file."
(interactive)
(let* ((sessions (event/parse-sessions))
(table-string (event/generate-table-by-track sessions))
(output-file (concat (file-name-sans-extension (buffer-file-name))
"-schedule-by-track.org")))
(event/write-table-to-file table-string output-file)
(message "Schedule table written to %s" output-file)))