summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSacha Chua <sacha@sachachua.com>2022-10-05 00:02:21 -0400
committerSacha Chua <sacha@sachachua.com>2022-10-05 00:02:21 -0400
commit4f695107c28d2d0ef069faba3061d736a72c3be9 (patch)
tree0a3f97550f7de52781af9fd42a315949049d0365
parent4b9096c7955cb0e57b89512e57227254fac14663 (diff)
downloademacsconf-el-4f695107c28d2d0ef069faba3061d736a72c3be9.tar.xz
emacsconf-el-4f695107c28d2d0ef069faba3061d736a72c3be9.zip
Tweak schedule validation and display
-rw-r--r--emacsconf-publish.el170
-rw-r--r--emacsconf-schedule.el151
-rw-r--r--emacsconf.el5
3 files changed, 184 insertions, 142 deletions
diff --git a/emacsconf-publish.el b/emacsconf-publish.el
index acb7239..fef5b90 100644
--- a/emacsconf-publish.el
+++ b/emacsconf-publish.el
@@ -43,7 +43,8 @@
(defun emacsconf-update-talk ()
"Publish the schedule page and the page for this talk."
(interactive)
- (emacsconf-upcoming-insert-or-update)
+ (when (functionp 'emacsconf-upcoming-insert-or-update)
+ (emacsconf-upcoming-insert-or-update))
(let ((info (emacsconf-get-talk-info-for-subtree)))
(emacsconf-generate-before-page info)
(emacsconf-generate-after-page info))
@@ -425,20 +426,19 @@ resources."
(emacsconf-get-talk-info))))
(with-temp-file (expand-file-name "schedule-details.md"
(expand-file-name emacsconf-year emacsconf-directory))
+ ;; By track
(let ((links
(mapconcat (lambda (track)
(concat (cadr track) ": "
(mapconcat (lambda (sec)
(format "<a href=\"#%s-%s\">%s</a>"
(car track) (car sec) (cadr sec)))
- '(("sat-am" "Sat AM")
- ("sat-pm" "Sat PM")
- ("sun-am" "Sun AM")
- ("sun-pm" "Sun PM"))
+ '(("sat" "Saturday")
+ ("sun" "Sunday"))
" - ")))
'(("gen" "General")
("dev" "Development"))
- " \n")))
+ " | ")))
(insert (mapconcat
(lambda (track)
(let* ((id (elt track 0))
@@ -446,8 +446,8 @@ resources."
(start (elt track 2))
(end (elt track 3))
(sequence (emacsconf-schedule-get-subsequence info start end))
- (sat (emacsconf-schedule-get-subsequence sequence "Saturday" "Sunday"))
- (sun (emacsconf-schedule-get-subsequence sequence "Sunday")))
+ (sat (cdr (emacsconf-schedule-get-subsequence sequence "Saturday" "Sunday")))
+ (sun (cdr (emacsconf-schedule-get-subsequence sequence "Sunday"))))
(format "\n\n<a name=\"%s\"></a>\n## %s\n\n%s\n\n"
id
label
@@ -463,15 +463,41 @@ resources."
section-label
(emacsconf-format-main-schedule
section-seq))))
- `(("sat-am" "Saturday morning - Dec 3" ,(cdr (emacsconf-schedule-get-subsequence sat nil "LUNCH")))
- ("sat-pm" "Saturday afternoon - Dec 3" ,(cdr (emacsconf-schedule-get-subsequence sat "LUNCH")))
- ("sun-am" "Sunday morning - Dec 4" ,(cdr (emacsconf-schedule-get-subsequence sun nil "LUNCH")))
- ("sun-pm" "Sunday afternoon - Dec 4" ,(cdr (emacsconf-schedule-get-subsequence sun "LUNCH"))))
+ `(("sat" "Saturday, Dec 3" ,sat)
+ ("sun" "Sunday, Dec 4" ,sun))
"\n\n")
)))
'(("gen" "General" "^GEN Sat" "^DEV Sat")
("dev" "Development" "^DEV Sat"))
- "\n")))))
+ "\n")))
+ (let ((by-day (sort (seq-remove (lambda (s) (string-match "^\\(GEN\\|DEV\\)" (plist-get s :title)))
+ info)
+ #'emacsconf-sort-by-scheduled)))
+ (insert
+ "\n<a name=\"by-day\"></a>\n\n# By day\n\n## Saturday\n"
+ ;; Everything all together
+ (emacsconf-format-main-schedule
+ (emacsconf-schedule-get-subsequence
+ by-day
+ "Saturday opening remarks"
+ "Saturday closing remarks"))
+ "\n\n## Sunday\n"
+ ;; Everything all together
+ (emacsconf-format-main-schedule
+ (emacsconf-schedule-get-subsequence
+ by-day
+ "Sunday opening remarks"
+ "Sunday closing remarks"))
+ ))))
+
+(defun emacsconf-publish-schedule ()
+ (interactive)
+ (emacsconf-generate-main-schedule-with-tracks)
+ (let ((default-directory emacsconf-directory))
+ (magit-status emacsconf-directory)
+ (magit-stage-modified)
+ (magit-commit-create (list "-m" (read-string "Commit message: ")))
+ (call-interactively #'magit-push-current-to-pushremote)))
(defun emacsconf-generate-main-schedule (&optional info)
(interactive)
@@ -777,7 +803,7 @@ resources."
extensions))))
(defun emacsconf-talks-csv ()
"Make a CSV of the talks.
-Columns are: slug,title,speakers,talk page url,video url,duration,sha."
+ Columns are: slug,title,speakers,talk page url,video url,duration,sha."
(interactive)
(require 'org-table)
(require 'compile-media)
@@ -842,71 +868,71 @@ Columns are: slug,title,speakers,talk page url,video url,duration,sha."
(defun emacsconf-generate-pad-template (emacsconf-info)
"Generate a template for copying and pasting into the pad.
- Writes it to pad-template.html."
+ Writes it to pad-template.html."
(interactive (list (emacsconf-get-talk-info)))
(let* ((talks (emacsconf-filter-talks emacsconf-info))
(text (concat
"<p>Conference info, how to watch/participate: https://emacsconf.org/2021/<br />
- Guidelines for conduct: https://emacsconf.org/conduct/</p>
+ Guidelines for conduct: https://emacsconf.org/conduct/</p>
- <p>Except where otherwise noted, the material on the EmacsConf pad are dual-licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International Public License; and the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) an later version.
- Copies of these two licenses are included in the EmacsConf wiki repository, in the COPYING.GPL and COPYING.CC-BY-SA files (https://emacsconf.org/COPYING/).</p>
+ <p>Except where otherwise noted, the material on the EmacsConf pad are dual-licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International Public License ; and the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) an later version.
+ Copies of these two licenses are included in the EmacsConf wiki repository, in the COPYING.GPL and COPYING.CC-BY-SA files (https://emacsconf.org/COPYING/).</p>
- <p>By contributing to this pad, you agree to make your contributions available under the above licenses. You are also promising that you are the author of your changes, or that you copied them from a work in the public domain or a work released under a free license that is compatible with the above two licenses. DO NOT SUBMIT COPYRIGHTED WORK WITHOUT PERMISSION.</p>
+ <p>By contributing to this pad, you agree to make your contributions available under the above licenses. You are also promising that you are the author of your changes, or that you copied them from a work in the public domain or a work released under a free license that is compatible with the above two licenses. DO NOT SUBMIT COPYRIGHTED WORK WITHOUT PERMISSION.</p>
- <p>
- This pad is here to be curated by everybody and its rough structure is like this:
- <ol><li>General info and license
- <li>A section for each talk -> please do add questions and notes
- <li>A general feedback section
- </ol>
- </p>
- "
+ <p>
+ This pad is here to be curated by everybody and its rough structure is like this:
+ <ol><li>General info and license
+ <li>A section for each talk -> please do add questions and notes
+ <li>A general feedback section
+ </ol>
+ </p>
+ "
(mapconcat
(lambda (o)
(let ((url (format "https://emacsconf.org/%s/talks/%s" emacsconf-year (plist-get o :slug))))
(format "-------------------------------------------------------------------------------------------------<br/><strong>Talk%s: %s</strong><br />
- Speaker(s): %s<br />
- Talk page: <a href=\"%s\">%s</a><br />
- Actual start of talk EST: &nbsp;&nbsp;&nbsp; Start of Q&A: &nbsp;&nbsp; End of Q&A: &nbsp;&nbsp;<br />
- <strong>Questions:</strong>
- Speakers may answer in any order or skip questions. As much as possible, put your questions at the top level instead of under another question. If adding an answer, please indicate [speaker] or your nick accordingly. Volunteers, please add new slots as ones get filled.<br />
- <ul>
- <li>Q1:&nbsp;<ul><li>A:&nbsp;</li></ul></li>
- <li>Q2:&nbsp;<ul><li>A:&nbsp;</li></ul></li>
- <li>Q3:&nbsp;<ul><li>A:&nbsp;</li></ul></li>
- <li>Q4:&nbsp;<ul><li>A:&nbsp;</li></ul></li>
- </ul>
-
- <strong>Links and other notes:</strong>
- <ul>
- <li>sample text</li>
- <li>sample text</li>
- <li>sample text</li>
- <li>sample text</li>
- </ul>
- " (plist-get o :slug) (plist-get o :title) (plist-get o :speakers) url url))) talks "<br/><br/>\n")
+ Speaker(s): %s<br />
+ Talk page: <a href=\"%s\">%s</a><br />
+ Actual start of talk EST: &nbsp ;&nbsp;&nbsp; Start of Q&A: &nbsp;&nbsp; End of Q&A: &nbsp;&nbsp;<br />
+ <strong>Questions:</strong>
+ Speakers may answer in any order or skip questions. As much as possible, put your questions at the top level instead of under another question. If adding an answer, please indicate [speaker] or your nick accordingly. Volunteers, please add new slots as ones get filled.<br />
+ <ul>
+ <li>Q1:&nbsp ;<ul><li>A:&nbsp;</li></ul></li>
+ <li>Q2:&nbsp ;<ul><li>A:&nbsp;</li></ul></li>
+ <li>Q3:&nbsp ;<ul><li>A:&nbsp;</li></ul></li>
+ <li>Q4:&nbsp ;<ul><li>A:&nbsp;</li></ul></li>
+ </ul>
+
+ <strong>Links and other notes:</strong>
+ <ul>
+ <li>sample text</li>
+ <li>sample text</li>
+ <li>sample text</li>
+ <li>sample text</li>
+ </ul>
+ " (plist-get o :slug) (plist-get o :title) (plist-get o :speakers) url url))) talks "<br/><br/>\n")
"<br/><br/>-------------------------------------------------------------------------------------------------<br/>
- <strong>General Feedback: What went well?</strong><br/><br/>
- <ul>
- <li>sample text</li>
- <li>sample text</li>
- <li>sample text</li>
- <li>sample text</li>
- </ul>
- <br /><br />
- -------------------------------------------------------------------------------------------------<br/>
- <strong>General Feedback: What to improve?</strong><br/><br/>
- <ul>
- <li>sample text</li>
- <li>sample text</li>
- <li>sample text</li>
- <li>sample text</li>
- </ul>
- <br/><br/>
- -------------------------------------------------------------------------------------------------<br/>
- <strong>Colophon:</strong>
- <ul></ul>")))
+ <strong>General Feedback: What went well?</strong><br/><br/>
+ <ul>
+ <li>sample text</li>
+ <li>sample text</li>
+ <li>sample text</li>
+ <li>sample text</li>
+ </ul>
+ <br /><br />
+ -------------------------------------------------------------------------------------------------<br/>
+ <strong>General Feedback: What to improve?</strong><br/><br/>
+ <ul>
+ <li>sample text</li>
+ <li>sample text</li>
+ <li>sample text</li>
+ <li>sample text</li>
+ </ul>
+ <br/><br/>
+ -------------------------------------------------------------------------------------------------<br/>
+ <strong>Colophon:</strong>
+ <ul></ul>")))
(with-current-buffer (find-file "pad-template.html")
(erase-buffer)
(insert text)
@@ -1027,11 +1053,11 @@ Columns are: slug,title,speakers,talk page url,video url,duration,sha."
(when (and emacsconf-public-media-directory slug (> (length (string-trim slug)) 0)
;; TODO: make this customizable
(shell-command
- (format "ssh front -- 'rm /var/www/media.emacsconf.org/%s/%s*; cp -n -l /var/www/media.emacsconf.org/%s/protected/%s* /var/www/media.emacsconf.org/%s/; chmod ugo+r /var/www/media.emacsconf.org/%s/ -R'"
- emacsconf-year slug
- emacsconf-year slug
- emacsconf-year
- emacsconf-year)))
+ (format "ssh front -- 'rm /var/www/media.emacsconf.org/%s/%s* ; cp -n -l /var/www/media.emacsconf.org/%s/protected/%s* /var/www/media.emacsconf.org/%s/; chmod ugo+r /var/www/media.emacsconf.org/%s/ -R'"
+ emacsconf-year slug
+ emacsconf-year slug
+ emacsconf-year
+ emacsconf-year)))
(when emacsconf-public-media-directory
(emacsconf-make-public-index (expand-file-name "index.html" emacsconf-public-media-directory))
(emacsconf-generate-playlist (expand-file-name "index.m3u" emacsconf-public-media-directory)
diff --git a/emacsconf-schedule.el b/emacsconf-schedule.el
index 94b61aa..f67f3fe 100644
--- a/emacsconf-schedule.el
+++ b/emacsconf-schedule.el
@@ -24,7 +24,7 @@
;;; Code:
-(defvar emacsconf-scheduling-strategies nil "List of scheduling functions.
+(defvar emacsconf-schedule-strategies nil "List of scheduling functions.
Each function should take the info and manipulate it as needed, returning the new info.")
(defvar emacsconf-schedule-max-time 30)
@@ -56,7 +56,7 @@ Each function should take the info and manipulate it as needed, returning the ne
(defun emacsconf-schedule-prepare (&optional info)
(emacsconf-schedule-based-on-info
(seq-reduce (lambda (prev val) (funcall val prev))
- emacsconf-scheduling-strategies
+ emacsconf-schedule-strategies
(or info (emacsconf-get-talk-info)))))
(defun emacsconf-schedule-summarize-breaks (&optional list)
@@ -79,12 +79,12 @@ Each function should take the info and manipulate it as needed, returning the ne
(emacsconf-schedule-tweaked-allocations '(("indieweb" . 20)
("maint" . 20)
("workflows" . 20)))
- (emacsconf-scheduling-strategies '(emacsconf-schedule-allocate-buffer-time
- emacsconf-schedule-override-breaks
- emacsconf-schedule-allocate-buffer-time-at-most-max-time
- emacsconf-schedule-allocate-max-time
- emacsconf-schedule-allocate-at-most
- emacsconf-schedule-tweak-allocations)))
+ (emacsconf-schedule-strategies '(emacsconf-schedule-allocate-buffer-time
+ emacsconf-schedule-override-breaks
+ emacsconf-schedule-allocate-buffer-time-at-most-max-time
+ emacsconf-schedule-allocate-max-time
+ emacsconf-schedule-allocate-at-most
+ emacsconf-schedule-tweak-allocations)))
(emacsconf-schedule-prepare info)))
(defun emacsconf-schedule-allocate-buffer-time-at-most-max-time (info)
@@ -122,7 +122,6 @@ Each function should take the info and manipulate it as needed, returning the ne
schedule))
'(hline)))
sched nil)))
-
(defun emacsconf-schedule-inflate-sexp (sequence &optional info include-time)
"Pairs with `emacsconf-schedule-dump-sexp'."
(setq info (or info (emacsconf-get-talk-info)))
@@ -130,65 +129,41 @@ Each function should take the info and manipulate it as needed, returning the ne
date)
(mapcar
(lambda (seq)
+ (unless (listp seq) (setq seq (list seq)))
+
(if include-time
(error "Not yet implemented")
- (cond
- ((eq seq 'break)
- (list :title "BREAK" :time emacsconf-schedule-break-time))
- ((eq seq 'lunch)
- (list :title "LUNCH" :time emacsconf-schedule-lunch-time))
- ((and (listp seq) (member (car seq) '(break lunch)) (stringp (cdr seq)))
- (if (string-match "-" (cdr seq))
- (setq date (format-time-string "%Y-%m-%d" (date-to-time (cdr seq))))
- (setcdr seq (concat date " " (cdr seq))))
- (list :title (if (eq (car seq) 'lunch) "LUNCH" "BREAK")
- :scheduled (format-time-string (cdr org-time-stamp-formats) (date-to-time (cdr seq)))
- :start-time (date-to-time (cdr seq))
- :fixed-time t
- :time (if (eq (car seq) 'lunch) emacsconf-schedule-lunch-time emacsconf-schedule-break-time)))
- ((and (listp seq) (member (car seq) '(break lunch)) (numberp (cdr seq)))
- (list :title (if (eq (car seq) 'lunch) "LUNCH" "BREAK")
- :time (string-to-number (cdr seq))))
- ;; Named thing with fixed time
- ((and (listp seq) (stringp (car seq)) (stringp (cdr seq)))
- (if (string-match "-" (cdr seq))
- (setq date (format-time-string "%Y-%m-%d" (date-to-time (cdr seq))))
- (setcdr seq (concat date " " (cdr seq))))
- (append
- (list :title (car seq)
- :scheduled (format-time-string (car org-time-stamp-formats) (date-to-time (cdr seq)))
- :start-time (date-to-time (cdr seq))
- :fixed-time t)
- (seq-find (lambda (o) (string= (plist-get o :title) (car seq))) info)
- ))
- ;; Named thing with duration
- ((and (listp seq) (stringp (car seq)) (numberp (cdr seq)))
+ (let ((start-prop (or (plist-get (cdr seq) :start)
+ (and (stringp (cdr seq)) (cdr seq))))
+ (time-prop (or (plist-get (cdr seq) :time) ; this is duration in minutes
+ (and (numberp (cdr seq)) (cdr seq))))
+ (track-prop (plist-get (cdr seq) :track)))
(append
- (list :title (car seq)
- :time (number-to-string (cdr seq)))
- (seq-find (lambda (o) (string= (plist-get o :title) (car seq))) info)))
- ;; Named thing
- ((stringp seq)
- (or (seq-find (lambda (o) (string= (plist-get o :title) seq)) info)
- (list :title seq)))
- ;; Slug with time
- ((and (listp seq) (symbolp (car seq)) (stringp (cdr seq)))
- (if (string-match "-" (cdr seq))
- (setq date (format-time-string "%Y-%m-%d" (date-to-time (cdr seq))))
- (setcdr seq (concat date " " (cdr seq))))
- (append (list :scheduled (format-time-string (cdr org-time-stamp-formats) (date-to-time (cdr seq)))
- :start-time (date-to-time (cdr seq))
- :fixed-time t)
- (assoc-default (car seq) by-assoc)))
- ;; Slug with duration
- ((and (listp seq) (symbolp (car seq)) (numberp (cdr seq)))
- (append (list :override-time t
- :time (number-to-string (cdr seq)))
- (assoc-default (car seq) by-assoc)))
- ;; Just the slug
- ((symbolp seq)
- (assoc-default seq by-assoc))
- (t (error "Unknown %s" (prin1-to-string seq))))))
+ ;; overriding
+ (when start-prop
+ (if (string-match "-" start-prop)
+ (setq date (format-time-string "%Y-%m-%d" (date-to-time start-prop)))
+ (setq start-prop (concat date " " start-prop)))
+ (list
+ :scheduled (format-time-string (cdr org-time-stamp-formats) (date-to-time start-prop))
+ :start-time (date-to-time start-prop)
+ :fixed-time t))
+ (when track-prop
+ (list :track track-prop))
+ (when time-prop
+ (list :time (if (numberp time-prop) (number-to-string time-prop) time-prop)))
+ ;; base entity
+ (cond
+ ((eq (car seq) 'lunch)
+ (list :title "LUNCH" :time (number-to-string emacsconf-schedule-lunch-time)))
+ ((eq (car seq) 'break)
+ (list :title "BREAK" :time (number-to-string emacsconf-schedule-break-time)))
+ ((symbolp (car seq))
+ (assoc-default (car seq) by-assoc))
+ ((stringp (car seq))
+ (or (seq-find (lambda (o) (string= (plist-get o :title) (car seq))) info)
+ (list :title (car seq))))
+ (t (error "Unknown %s" (prin1-to-string seq))))))))
sequence)))
(defun emacsconf-format-schedule-summary-row (o)
@@ -268,6 +243,7 @@ Each function should take the info and manipulate it as needed, returning the ne
'rect
`((x . ,x)
(y . ,y)
+ (opacity . "0.8")
(width . ,(if vertical width size))
(height . ,(1- (if vertical size height)))
(stroke . "black")
@@ -364,9 +340,10 @@ Each function should take the info and manipulate it as needed, returning the ne
(seq-subseq info
(or start-position 0)
(if end
- (seq-position (seq-subseq info (or start-position 0))
- end
- (lambda (o match) (string-match match (plist-get o :title))))
+ (+ (or start-position 0)
+ (seq-position (seq-subseq info (or start-position 0))
+ end
+ (lambda (o match) (string-match match (plist-get o :title)))))
(length info)))))
;;; Schedule summary
@@ -528,6 +505,7 @@ Both start and end time are tested."
(seq-group-by #'identity sched-slugs))))
(append
(emacsconf-schedule-validate-time-constraints sched)
+ (emacsconf-schedule-validate-live-q-and-a-sessions-are-staggered sched)
(when diff
(list (concat "Missing talks: " (string-join diff ", "))))
(when dupes
@@ -539,7 +517,13 @@ Both start and end time are tested."
(plist-put day :tracks
(mapcar
(lambda (track)
- (apply #'emacsconf-schedule-get-subsequence schedule track))
+ (if (stringp track)
+ ;; track property
+ (seq-filter (lambda (o) (string= (or (plist-get o :track) (car (plist-get day :tracks)))
+ track))
+ schedule)
+ ;; start and end regexp
+ (apply #'emacsconf-schedule-get-subsequence schedule track)))
(plist-get day :tracks)))
day)
tracks))
@@ -595,6 +579,35 @@ Both start and end time are tested."
"")
(or (plist-get o :availability) "")))))
+(defvar emacsconf-schedule-validate-live-q-and-a-sessions-buffer 5 "Number of minutes' allowance for a streamer to adjust audio and get set up.
+Try to avoid overlapping the start of live Q&A sessions.")
+(defun emacsconf-schedule-validate-live-q-and-a-sessions-are-staggered (schedule)
+ "Return nil if there are no errors.
+Try to avoid overlapping the start of live Q&A sessions."
+ (when emacsconf-schedule-validate-live-q-and-a-sessions-buffer
+ (let (last-end)
+ (delq nil
+ (mapcar (lambda (o)
+ (prog1
+ (when (and last-end
+ (time-less-p
+ (plist-get o :end-time)
+ (time-add last-end (seconds-to-time (* emacsconf-schedule-validate-live-q-and-a-sessions-buffer 60)))))
+ (plist-put o :invalid (format "%s live Q&A starts at %s within %d minutes of previous live Q&A at %s"
+ (plist-get o :slug)
+ (format-time-string "%m-%d %-l:%M"
+ (plist-get o :end-time))
+ emacsconf-validate-live-q-and-a-sessions-buffer
+ (format-time-string "%m-%d %-l:%M"
+ last-end)))
+ (plist-get o :invalid))
+ (setq last-end (plist-get o :end-time))))
+ (sort
+ (seq-filter (lambda (o) (string-match "live" (or (plist-get o :q-and-a) "")))
+ schedule)
+ (lambda (a b)
+ (time-less-p (plist-get a :end-time) (plist-get b :end-time)))
+ ))))))
(defvar emacsconf-schedule-plan nil "Sequence of talks.")
(provide 'emacsconf-schedule)
;;; emacsconf-schedule.el ends here
diff --git a/emacsconf.el b/emacsconf.el
index 1844713..ca0074b 100644
--- a/emacsconf.el
+++ b/emacsconf.el
@@ -199,6 +199,7 @@
(let ((heading (org-heading-components))
(field-props '((:title "ITEM")
(:talk-id "TALK_ID")
+ (:track "TRACK")
(:slug "SLUG")
(:video-slug "VIDEO_SLUG")
(:public "PUBLIC")
@@ -369,9 +370,11 @@
(let ((time-a (plist-get a :start-time))
(time-b (plist-get b :start-time)))
(cond
+ ((null time-b) t)
+ ((null time-a) nil)
((time-less-p time-a time-b) t)
((time-less-p time-b time-a) nil)
- (t (< (plist-get a :point) (plist-get b :point))))))
+ (t (< (or (plist-get a :point) 0) (or (plist-get b :point) 0))))))
(defun emacsconf-get-talk-info ()
(with-current-buffer (find-file-noselect emacsconf-org-file)