summaryrefslogtreecommitdiffstats
path: root/2020/organizers-notebook.org
diff options
context:
space:
mode:
authorSacha Chua <sacha@sachachua.com>2020-12-12 00:52:38 -0500
committerSacha Chua <sacha@sachachua.com>2020-12-12 00:52:38 -0500
commit4dac85ef27c35d526edcb5c6bc1965bb26f84472 (patch)
treefb83de634859257c218c8a779ebb16b3ac8313fc /2020/organizers-notebook.org
parentab8dfc5d57475c546dbb0dc7b92194c230610fce (diff)
downloademacsconf-wiki-4dac85ef27c35d526edcb5c6bc1965bb26f84472.tar.xz
emacsconf-wiki-4dac85ef27c35d526edcb5c6bc1965bb26f84472.zip
More tweaks to code
Diffstat (limited to '')
-rw-r--r--2020/organizers-notebook.org413
1 files changed, 370 insertions, 43 deletions
diff --git a/2020/organizers-notebook.org b/2020/organizers-notebook.org
index d69c4d2d..5e5a3b41 100644
--- a/2020/organizers-notebook.org
+++ b/2020/organizers-notebook.org
@@ -1,12 +1,14 @@
#+todo: TODO(t) INPROGRESS(i) | DONE(d) CANCELLED(c)
#+OPTIONS: h:6
-#+PROPERTY: header-args :results silent :exports code
+#+PROPERTY: header-args :results silent :exports code :tangle yes
#+begin_export md
<!-- automatically generated from organizers-notebook.org. Please edit that file instead. -->
[[!toc levels=4]]
#+end_export
+[[elisp:(progn (org-md-export-to-markdown) (org-babel-tangle))][Export and tangle]]
+
* Tasks
** Manually transcribe
Either subtitles (with timestamps) or a text transcript (no timestamps) is perfectly okay.
@@ -69,32 +71,26 @@ organizers-notebook.org back to the repo.
- [ ] [[./subtitles/emacsconf-2020--41-opening-remarks-autogen.sbv]]
- [ ] [[./subtitles/emacsconf-2020--42-closing-remarks-autogen.sbv]]
-** DONE Create tasks for each of the subtitles
- CLOSED: [2020-12-09 Wed 15:42]
- :LOGBOOK:
- - State "DONE" from "TODO" [2020-12-09 Wed 15:42]
- :END:
-** DONE Link compressed videos on each talk page
- CLOSED: [2020-12-09 Wed 15:42]
- :LOGBOOK:
- - State "DONE" from "TODO" [2020-12-09 Wed 15:42]
- :END:
+* Assumptions and settings
+
+Note that re-evaluating a defvar won't change the value, so if you want to change the value after this is already loaded, use =(setq ...)=.
+
#+begin_src emacs-lisp
- (mapc (lambda (o)
- (if (string-match "\\(emacsconf-2020--\\([0-9]+\\).*?\\)-vp9-q56-original-audio\\.webm" o)
- (let ((talk-id (match-string 2 o))
- (base (match-string 1 o)))
- (find-file (expand-file-name (concat talk-id ".md") "~/vendor/emacsconf-wiki/2020/info"))
- (goto-char (point-min))
- (when (re-search-forward base nil t)
- (forward-line 1)
- (beginning-of-line)
- (unless (looking-at "\\[Download")
- (insert (format "[Download compressed %s.webm video (%s)](https://media.emacsconf.org/2020/%s)\n"
- (if (string-match "questions" o) "Q&A " "")
- (file-size-human-readable (file-attribute-size (file-attributes o)))
- (file-name-nondirectory o))))))))
- (directory-files "~/vendor/emacsconf-original-audio" t "webm"))
+(defvar conf/buffer-minutes 3 "Number of minutes to use as a buffer between talks.")
+(defvar conf/timezones '("EST" "America/Los_Angeles" "UTC" "CET" "Asia/Singapore") "List of timezones")
+(defvar conf/autogenerate-talk-pages nil "Set this to t at the beginning of the conference, when we're still autogenerating individual talk pages.
+Otherwise you might overwrite hand-edited talk pages.")
+(defvar conf/collaborative-pad "https://etherpad.wikimedia.org/p/emacsconf-2020" "URL of collaborative pad.")
+(defvar conf/streaming-nick "bandali" "IRC nick of main organizer in charge of streaming.")
+(defvar conf/topic-templates nil "List of (channel topic-template) entries for mass-setting channel topics.")
+(defvar conf/rooms '(("A" "http://example.org?room=a")
+ ("B" "http://example.org?room=b")
+ ("C" "http://example.org?room=c"))
+ "List of (code join-url) entries. Room codes should be uppercase.") ; actually set this in organizers' wiki index.org
+(setq conf/topic-templates
+ '(("#emacsconf" "EmacsConf 2020 is over, thanks for joining! | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates")
+ ("#emacsconf-accessible" "EmacsConf 2020 is over. Thanks for making it more accessible! | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates")
+ ("#emacsconf-org" "EmacsConf2020 is over, thanks for joining! | Dedicated channel for EmacsConf organizers and speakers | this is intended as an internal, low-traffic channel; for main discussion around EmacsConf, please join #emacsconf | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates")))
#+end_src
* Workflows
@@ -106,6 +102,333 @@ STREAM - main organizer, CHECK - secondary organizer or volunteer, PAD - organiz
- Collect e-mail addresses of accepted speakers into a list for easy pasting into Bcc (organizers' private wiki)
- See submissions.org for Org scheduling code; 3 minutes of buffer was okay last time, but more would be better for Q&A; opening remarks time could be trimmed
+**** Code for scheduling
+#+begin_src emacs-lisp :exports code :results none
+(defun conf/get-talk-info ()
+ (let (talk results)
+ (org-map-entries (lambda ()
+ (let ((heading (org-heading-components)))
+ (cond
+ ((and (elt heading 2) (or (null talk)
+ (<= (car heading)
+ (plist-get talk :level)))) ;; has a todo, therefore is a talk
+ (when talk (setq results (cons talk results)))
+ (setq talk (list
+ :type 'talk
+ :title (elt heading 4)
+ :talk-id (org-entry-get (point) "TALK_ID")
+ :status (elt heading 2)
+ :level (car heading)
+ :scheduled (org-entry-get (point) "SCHEDULED")
+ :duration (org-entry-get (point) "DURATION")
+ :time (org-entry-get (point) "MIN_TIME")
+ :speakers (org-entry-get (point) "NAME"))))
+ ((string-match "^ *Talk information *$" (elt heading 4))
+ (plist-put talk :info
+ (org-export-as 'md t nil t)))
+ ((or (null talk) (< (car heading) (plist-get talk :level))) ;; heading above
+ (when talk
+ (setq results (cons talk results))
+ (setq talk nil))
+ (setq results (cons
+ (list :type 'headline
+ :level (car heading)
+ :speakers (org-entry-get (point) "NAME")
+ :duration (org-entry-get (point) "DURATION")
+ :talk-id (org-entry-get (point) "TALK_ID")
+ :title (elt heading 4)
+ :scheduled (org-entry-get (point) "SCHEDULED"))
+ results))))))
+ nil 'tree)
+ (when talk (setq results (cons talk results)))
+ (reverse results)))
+
+(defun conf/filter-talks (list)
+ "Return only talk info in LIST."
+ (seq-filter
+ (lambda (talk) (eq (plist-get talk :type) 'talk))
+ list))
+
+(defun conf/get-talk-info-from-file (&optional filename)
+ (with-temp-buffer
+ (insert-file-contents (or filename "submissions.org"))
+ (org-mode)
+ (org-show-all)
+ (goto-char (point-min))
+ (goto-char (org-find-property "ID" "talks"))
+ (conf/get-talk-info)))
+
+
+(defun conf/find-talk (filter &optional info)
+ (setq info (or info (conf/filter-talks conf/info)))
+ (when (stringp filter) (setq filter (list filter)))
+ (or (seq-find (lambda (o) (string= (plist-get o :talk-id) (car filter))) info)
+ (seq-find (lambda (o)
+ (let ((case-fold-search t)
+ (all (mapconcat (lambda (f) (plist-get o f)) '(:title :speakers :talk-id) " ")))
+ (null (seq-contains-p
+ (mapcar (lambda (condition) (string-match condition all)) filter)
+ nil))))
+ info)))
+
+(defun conf/goto-talk-id (id)
+ (goto-char (org-find-property "TALK_ID" id)))
+
+(defun conf/assign-ids ()
+ "Assign numeric talk IDs."
+ (interactive)
+ (goto-char (point-min))
+ ;; Determine the maximum ID assigned so far
+ (let ((id
+ (1+
+ (apply 'max
+ (or (mapcar
+ 'string-to-number
+ (org-map-entries
+ (lambda ()
+ (let ((org-trust-scanner-tags t))
+ (org-entry-get (point) "TALK_ID"))) "TALK_ID>0" 'file))
+ '(0))))))
+ (goto-char (point-min))
+ (while (re-search-forward "^ *:NAME: " nil t)
+ (unless (org-entry-get (point) "TALK_ID")
+ (org-set-property "TALK_ID" (format "%02d" id))
+ (org-set-property "CUSTOM_ID" (format "talk%02d" id))
+ (setq id (1+ id))))))
+
+(defun conf/update-talks ()
+ "Update times, tables, and schedules."
+ (interactive)
+ (save-excursion
+ (conf/update-times)
+ (conf/update-tables)
+ (conf/update-schedules)))
+
+(defun conf/update-times ()
+ "Check whether we need more time or less time based on TARGET_TIME and MIN_TIME_SUM."
+ (goto-char (point-min))
+ (org-map-entries
+ (lambda ()
+ (when (org-entry-get (point) "TARGET_TIME")
+ (conf/org-sum-min-time-in-subtree)
+ (org-entry-put
+ (point)
+ "DIFFERENCE"
+ (let ((diff
+ (-
+ (string-to-number (org-entry-get (point) "TARGET_TIME"))
+ (string-to-number (org-entry-get (point) "MIN_TIME_SUM")))))
+ (cond
+ ((> diff 0) (format "Extra: %d" diff))
+ ((< diff 0) (format "Needs: %d" (- diff)))
+ (t "")))))) nil 'file))
+
+(defun conf/update-tables ()
+ "Update the time checks and table reports."
+ (goto-char (point-min))
+ (while (re-search-forward "#\\+CALL: check_time()" nil t)
+ (org-ctrl-c-ctrl-c))
+ (goto-char (point-min))
+ (while (re-search-forward "#\\+BEGIN: columnview" nil t)
+ (org-ctrl-c-ctrl-c)))
+
+(defun conf/update-schedules ()
+ "Schedule the talks based on the MIN_TIME and 3 minutes of buffer.
+Talks with a FIXED_TIME property are not moved."
+ (interactive)
+ (goto-char (org-find-exact-headline-in-buffer "Talks"))
+ (let (current-time scheduled end-time duration (buffer (seconds-to-time (* conf/buffer-minutes 60)))) ;; assumption: 3 minutes between talks
+ (org-map-entries (lambda ()
+ (if (org-entry-get (point) "FIXED_TIME")
+ (setq current-time (org-get-scheduled-time (point))))
+ (when (org-entry-get (point) "MIN_TIME")
+ (setq duration (* (string-to-number (org-entry-get (point) "MIN_TIME")) 60)
+ end-time (time-add current-time (seconds-to-time duration)))
+ (org-set-property "SCHEDULED" (format "%s-%s" (org-format-time-string "%Y-%m-%d %H:%M" current-time)
+ (org-format-time-string "%H:%M" end-time)))
+ (setq current-time (time-add end-time buffer))))
+ nil 'tree)))
+
+(defun conf/org-sum-min-time-in-subtree ()
+ "Add up all the MIN_TIME properties of headings underneath the current one
+The total is written to the MIN_TIME_SUM property of this heading"
+ (interactive)
+ (org-entry-put
+ (point)
+ "MIN_TIME_SUM"
+ (save-excursion
+ (format "%d"
+ (apply
+ '+
+ (mapcar 'string-to-number
+ (delq nil
+ (org-map-entries
+ (lambda () (org-entry-get (point) "MIN_TIME")) nil 'tree))))))))
+
+#+end_src
+
+**** Generate schedule file
+
+ #+begin_src emacs-lisp :results none :eval yes :exports code :tangle "conf.el"
+ (defun conf/format-talk-link (talk)
+ (and talk (if (plist-get talk :talk-id)
+ (format "<a href=\"/2020/talks/%s\">%s</a>"
+ (plist-get talk :talk-id)
+ (plist-get talk :title))
+ (plist-get talk :title))))
+
+ (defun conf/format-talk-info-as-schedule (info)
+ (format "<table width=\"100%%\">%s</table>"
+ (mapconcat
+ (lambda (o)
+ (let* ((time-fmt "%l:%M %p")
+ (timestamp (org-timestamp-from-string (plist-get o :scheduled)))
+ (start (if timestamp (format-time-string time-fmt (org-timestamp-to-time (org-timestamp-split-range timestamp))) ""))
+ (end (if timestamp (format-time-string time-fmt (org-timestamp-to-time (org-timestamp-split-range timestamp t))) ""))
+ (title (plist-get o :title))
+ (speakers (plist-get o :speakers)))
+ (if (eq (plist-get o :type) 'headline)
+ (format "<tr><td colspan=\"4\"><strong>%s<strong></td></tr>"
+ (if (plist-get o :talk-id)
+ (conf/format-talk-link o)
+ title))
+ (format "<tr><td width=100>~%s</td><td width=100>~%s</td><td>%s</td><td>%s</td></tr>"
+ start end (conf/format-talk-link o) speakers))))
+ (cdr info) "\n")))
+
+ (defun conf/filter-talks (info)
+ (seq-filter (lambda (o) (plist-get o :talk-id)) info))
+
+ (defun conf/split-out-talk-information ()
+ (interactive)
+ (let ((talks (conf/filter-talks conf/info)))
+ (mapc (lambda (o)
+ (with-temp-buffer
+ (insert
+ (format "# %s\n%s\n\n%s"
+ (plist-get o :title)
+ (plist-get o :speakers)
+ (plist-get o :info)))
+ (write-file (expand-file-name (format "%s.md" (plist-get o :talk-id)) "info"))))
+ talks)))
+
+ (defun conf/format-talk-pages (info)
+ (let* ((talks (conf/filter-talks info))
+ (next-talks (cdr talks))
+ (prev-talks (cons nil talks)))
+ (mapc (lambda (o)
+ (with-temp-buffer
+ (let* ((timestamp (org-timestamp-from-string (plist-get o :scheduled)))
+ (next-talk (conf/format-talk-link (pop next-talks)))
+ (prev-talk (conf/format-talk-link (pop prev-talks)))
+ (schedule (mapconcat
+ (lambda (tz)
+ (format "%s - %s"
+ (format-time-string "%A, %b %e %Y, ~%l:%M %p"
+ (org-timestamp-to-time (org-timestamp-split-range timestamp)) tz)
+ (format-time-string "%l:%M %p %Z"
+ (org-timestamp-to-time (org-timestamp-split-range timestamp t)) tz)))
+ conf/timezones
+ " \n"))
+ (nav-links (format "Back to the [[schedule]] \n%s%s"
+ (if prev-talk (format "Previous: %s \n" prev-talk) "")
+ (if next-talk (format "Next: %s \n" next-talk) ""))))
+ (insert (format "[[%s title=\"%s\"]]
+ [[%s copyright=\"Copyright &copy; 2020 %s\"]]
+
+ <!-- To edit the talk information, change /2020/info/TALKID.md. Boilerplate automatically generated from submissions.org using conf/generate-schedule-files --->\n
+
+ %s
+
+ [[!inline pages=\"internal(2020/info/%s)\" raw=\"yes\"]]
+
+ %s
+
+ %s
+
+ "
+ "!meta"
+ (replace-regexp-in-string "\"" "\\\\\"" (plist-get o :title))
+ "!meta"
+ (plist-get o :speakers)
+ nav-links
+ (plist-get o :talk-id)
+ schedule
+ nav-links)))
+ (write-file (format "talks/%s.md" (plist-get o :talk-id)))))
+ talks)))
+
+ (defun conf/generate-pad-template ()
+ (interactive)
+ (let ((talks (conf/filter-talks conf/info)))
+ (with-current-buffer (find-file "pad-template.md")
+ (erase-buffer)
+ (insert
+ (concat
+ "Conference info, how to watch/participate: <https://emacsconf.org/2020/>
+ Code of conduct: <https://emacsconf.org/conduct/>
+
+ "
+ (mapconcat
+ (lambda (o)
+ (format "**%s**
+ Speaker(s): %s
+ Talk page: <https://emacsconf.org/2020/schedule/%s>
+
+ ,*Questions:*
+
+ ,* Put your questions here, most recent on top
+ ,* sample text
+
+ ,*Links:*
+
+ ,* sample text
+ ,* sample text
+
+ ,*Other notes:*
+
+ ,* sample text
+ ,* sample text
+ <br>
+ <br>
+ " (plist-get o :title) (plist-get o :speakers) (plist-get o :talk-id))) talks " \n \n")))
+ (save-buffer))))
+
+ (defun conf/generate-talks-page ()
+ (let ((info conf/info))
+ (with-temp-buffer
+ (find-file "talk-details.md")
+ (erase-buffer)
+ (insert (format "<table><thead><th>Duration</th><th>Title</th><th>Speakers</th></thead><tbody>%s</tbody></table>"
+ (mapconcat
+ (lambda (o)
+ (let* ((title (plist-get o :title))
+ (speakers (plist-get o :speakers)))
+ (if (null (plist-get o :talk-id))
+ (format "<tr><td colspan=\"3\">%s</td></tr>" (conf/format-talk-link o))
+ (format "<tr><td>%s</td><td>%s</td><td>%s</td><tr>"
+ (plist-get o :duration)
+ (conf/format-talk-link o)
+ (plist-get o :speakers)))))
+ info "\n")))
+ (save-buffer))))
+
+ (defun conf/generate-schedule-files (&optional filename)
+ (interactive)
+ (with-temp-buffer
+ (insert (conf/format-talk-info-as-schedule conf/info))
+ (write-file "schedule-details.md"))
+ (when conf/autogenerate-talk-pages (conf/format-talk-pages conf/info)))
+ #+end_src
+
+Set the info based on submissions.org.
+
+#+begin_src emacs-lisp
+(setq conf/info (conf/get-talk-info-from-file "submissions.org"))
+#+end_src
+
+ #+RESULTS:
+
*** Before the conference
- Do tech checks and get alternative ways to contact speakers (phone number? IRC nick? Something that goes ding?)
@@ -219,20 +542,7 @@ STREAM - main organizer, CHECK - secondary organizer or volunteer, PAD - organiz
***** Load data
#+begin_src emacs-lisp :results silent
- (defvar conf/collaborative-pad "https://etherpad.wikimedia.org/p/emacsconf-2020" "URL of collaborative pad.")
- (defvar conf/topic-templates nil "List of (channel topic-template) entries for mass-setting channel topics.")
- (defvar conf/streaming-nick "bandali" "IRC nick of main organizer in charge of streaming.")
- "List of (code join-url) entries, one for each meeting room.") ;; Set this in a private file
- (defvar conf/channels nil "List of IRC channels for broadcasts.")
- (defvar conf/info nil "List of plists with the following keys: `:talk-id', `:name', `:speakers', and other info.") ; Set from submissions.org
- (setq conf/topic-templates
- '(("#emacsconf" "EmacsConf 2020 is over, thanks for joining! | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates")
- ("#emacsconf-accessible" "EmacsConf 2020 is over. Thanks for making it more accessible! | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates")
- ("#emacsconf-org" "EmacsConf2020 is over, thanks for joining! | Dedicated channel for EmacsConf organizers and speakers | this is intended as an internal, low-traffic channel; for main discussion around EmacsConf, please join #emacsconf | Subscribe to https://lists.gnu.org/mailman/listinfo/emacsconf-discuss for updates")))
- (defvar conf/rooms '(("A" "http://example.org?room=a")
- ("B" "http://example.org?room=b")
- ("C" "http://example.org?room=c"))
- "List of (code join-url) entries. Room codes should be uppercase.") ; set from organizers' wiki index.org
+ (defvar conf/info nil "List of plists with the following keys: `:talk-id', `:name', `:speakers', and other info.") ; Set from submissions.org
#+end_src
***** Announce topics
@@ -299,7 +609,7 @@ STREAM - main organizer, CHECK - secondary organizer or volunteer, PAD - organiz
(defun erc-cmd-BROADCAST (&rest message)
"Say MESSAGE in all the conference channels."
- (conf/erc-with-channels conf/channels
+ (conf/erc-with-channels (mapcar 'car conf/topic-templates)
(erc-send-message (s-join " " message))))
#+end_src
@@ -399,7 +709,7 @@ Another collaborative pad
Usage: compress-video.sh input-filename.webm output-filename.webm
- #+begin_src sh :tangle compress-video.sh
+ #+begin_src sh :eval no :tangle compress-video.sh
Q=56
nice ffmpeg -y -i $1 -c:v libvpx-vp9 -b:v 0 -crf $Q -aq-mode 2 -an -tile-columns 0 -tile-rows 0 -frame-parallel 0 -cpu-used 8 -auto-alt-ref 1 -lag-in-frames 25 -g 999 -pass 1 -f webm -threads 8 /dev/null &&
nice ffmpeg -y -i $1 -c:v libvpx-vp9 -b:v 0 -crf $Q -c:a copy -tile-columns 2 -tile-rows 2 -frame-parallel 0 -cpu-used -5 -auto-alt-ref 1 -lag-in-frames 25 -pass 2 -g 999 -threads 8 $2
@@ -407,7 +717,7 @@ Another collaborative pad
Here's the original version which compresses audio too. Usage: compress-video-compressed-audio.sh input-filename.webm output-filename.webm
- #+begin_src sh :tangle compress-video-compressed-audio.sh
+ #+begin_src sh :eval no :tangle compress-video-compressed-audio.sh
Q=56
nice ffmpeg -y -i $1 -c:v libvpx-vp9 -b:v 0 -crf $Q -aq-mode 2 -an -tile-columns 0 -tile-rows 0 -frame-parallel 0 -cpu-used 8 -auto-alt-ref 1 -lag-in-frames 25 -g 999 -pass 1 -f webm -threads 8 /dev/null &&
nice ffmpeg -y -i $1 -c:v libvpx-vp9 -b:v 0 -crf $Q -aq-mode 2 -c:a libopus -b:a 12k -tile-columns 2 -tile-rows 2 -frame-parallel 0 -cpu-used -5 -auto-alt-ref 1 -lag-in-frames 25 -pass 2 -g 999 -threads 8 $2
@@ -463,6 +773,23 @@ Take advantage of provided scripts or autogenerated files
ssh front 'sudo -iu ikiwiki ikiwiki --setup ~ikiwiki/emacsconf.setup'
#+end_src
+
+** COMMENT Copyright & License
+
+ Copyright (C) 2020 Sacha Chua
+
+ The EmacsConf 2020 organizers' notebook is part of the EmacsConf
+ wiki, and is 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) any later
+ version.
+
+ A copy of these two licenses is available on the EmacsConf wiki, in
+ the [[https://emacsconf.org/COPYING.CC-BY-SA][COPYING.CC-BY-SA]] and [[https://emacsconf.org/COPYING.GPL][COPYING.GPL]] files.
+
+** COMMENT Local variables
+
# Local Variables:
# org-indent-mode: t
# org-indent-indentation-per-level: 2