summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--emacsconf-publish.el15
-rw-r--r--emacsconf-upcoming.el129
-rw-r--r--emacsconf.el188
3 files changed, 324 insertions, 8 deletions
diff --git a/emacsconf-publish.el b/emacsconf-publish.el
index 7a54f03..55b3458 100644
--- a/emacsconf-publish.el
+++ b/emacsconf-publish.el
@@ -165,7 +165,7 @@
(if (eq (plist-get talk :format) 'wiki)
(concat s " \n")
(concat "<li>" s "</li>")))
- (emacsconf-link-file-formats-as-list talk (or extensions emacsconf-published-extensions))
+ (emacsconf-link-file-formats-as-list talk (or extensions emacsconf-main-extensions))
"")
:poster (and video-file (format "https://media.emacsconf.org/%s/%s.png" (plist-get talk :conf-year) (file-name-base video-file)))
:toobnix-info (if (plist-get talk :toobnix-url)
@@ -384,7 +384,7 @@ ${info}
(pcase emacsconf-publishing-phase
('program "<tr><th>Status</th><th>Title<th><th>Speaker(s)</th></tr>")
('schedule "<tr><th>Status</th><th>Start</th><th>Title</th><th>Speaker(s)</th></tr>")
- ('resources "<tr><th>Title</th><th>Speaker(s)</th><th>Resources</th></tr>"))
+ ('resources "<tr><th>Title</th><th>Speaker(s)</th><th>Resources</th></tr>"))
(mapconcat
(lambda (o)
(let* ((time-fmt "%l:%M %p")
@@ -430,7 +430,7 @@ ${info}
(emacsconf-link-file-formats-as-list
(append o
(list :base-url (format "%s%s/" emacsconf-media-base-url emacsconf-year)))
- (append emacsconf-published-extensions '("--main.webm")))
+ (append emacsconf-main-extensions '("--main.webm")))
"")))))))
(seq-remove (lambda (o) (string= (plist-get o :status) "CANCELLED"))
(cdr info))
@@ -538,7 +538,7 @@ ${info}
:track-base-url
(format "/%s/captions/" (plist-get f :conf-year)))
f)
- emacsconf-published-extensions)
+ emacsconf-main-extensions)
"")
(if (plist-get f :qa-public)
(emacsconf-index-card
@@ -595,13 +595,12 @@ ${info}
"")
(mapconcat
(lambda (lang)
- (let ((lang-file (concat (file-name-sans-extension filename) "_" (car lang) (file-name-extension filename))))
+ (let ((lang-file (concat (file-name-sans-extension filename) "_" (car lang) "." (file-name-extension filename))))
(if (file-exists-p lang-file)
- (format "<track label=\"%s\" kind=\"captions\" srclang=\"%s\" src=\"%s--main_%s.vtt\" />"
+ (format "<track label=\"%s\" kind=\"captions\" srclang=\"%s\" src=\"%s\" />"
(cdr lang)
(car lang)
- (concat (or track-base-url "") (file-name-nondirectory lang-file))
- (car lang))
+ (concat (or track-base-url "") (file-name-nondirectory lang-file)))
"")))
'(("fr" . "French") ("ja" . "Japanese"))
"")))
diff --git a/emacsconf-upcoming.el b/emacsconf-upcoming.el
new file mode 100644
index 0000000..b94dc00
--- /dev/null
+++ b/emacsconf-upcoming.el
@@ -0,0 +1,129 @@
+;;; emacsconf-upcoming.el --- Update upcoming.org with information about the next talks -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Sacha Chua
+
+;; Author: Sacha Chua <sacha@sachachua.com>
+;; Keywords: data
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of 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.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(defun emacsconf-upcoming-update-file ()
+ (interactive)
+ (with-current-buffer (find-file-noselect emacsconf-upcoming-file)
+ (save-excursion
+ (org-map-entries #'emacsconf-upcoming-insert-or-update "SLUG={.}")
+)))
+
+(defun emacsconf-upcoming-add-subtree ()
+ (interactive)
+ (org-map-entries #'emacsconf-upcoming-insert-or-update "SLUG={.}" 'tree)
+ (save-excursion
+ (with-current-buffer (find-file-noselect emacsconf-upcoming-file)
+ (emacsconf-upcoming-sort))))
+
+(defun emacsconf-upcoming-sort ()
+ (interactive)
+ ;; Sort
+ (goto-char (point-min))
+ (when (looking-at "\\*") (save-excursion (insert "\n"))) ;; Need to be before the first heading.
+ (org-sort-entries
+ nil ?f
+ (lambda () (cons (org-entry-is-done-p) (org-entry-get (point) "SCHEDULED")))
+ (lambda (a b)
+ (cond
+ ((and (car a) (car b)) (string< (cdr a) (cdr b)))
+ ((car b) t)
+ ((car a) nil) ; move done things to the bottom
+ (t (string< (cdr a) (cdr b)))))))
+
+(defun emacsconf-upcoming-insert-or-update-from-slug (slug)
+ (interactive (list (emacsconf-complete-talk)))
+ (save-excursion
+ (emacsconf-with-talk-heading slug
+ (emacsconf-upcoming-insert-or-update nil nil))))
+
+(defun emacsconf-upcoming-insert-or-update (&optional info sort)
+ (interactive (list nil nil))
+ (let ((info
+ (or info
+ (when (buffer-file-name)
+ (if (string= (expand-file-name (buffer-file-name)) emacsconf-org-file)
+ (emacsconf-get-talk-info-for-subtree)
+ (let ((slug (org-entry-get (point) "SLUG")))
+ (emacsconf-with-talk-heading slug
+ (emacsconf-get-talk-info-for-subtree)))))))
+ pos)
+ (when (and (plist-get info :slug) emacsconf-upcoming-file)
+ (with-current-buffer (find-file-noselect emacsconf-upcoming-file)
+ (setq pos (org-find-property "SLUG" (plist-get info :slug)))
+ (if pos
+ (goto-char pos)
+ (goto-char (point-max))
+ (unless (bolp) (insert "\n"))
+ (insert (format "* TODO %s: %s - %s\n"
+ (plist-get info :slug)
+ (plist-get info :title)
+ (plist-get info :speakers))))
+ (org-todo (plist-get info :status))
+ (org-entry-put (point) "SLUG" (plist-get info :slug))
+ (org-entry-put (point) "URL" (concat emacsconf-base-url emacsconf-year "/talks/" (plist-get info :slug)))
+ (org-set-property "SCHEDULED" (plist-get info :scheduled))
+ (unless (org-entry-get (point) "INTRO_NOTE")
+ (org-entry-put (point) "INTRO_NOTE" (plist-get info :intro-note)))
+ (when (or (plist-get info :pronouns) (plist-get info :pronunciation))
+ (unless (org-entry-get (point) "OTHER_INFO")
+ (org-entry-put (point) "OTHER_INFO"
+ (string-join (delq nil (list (plist-get info :pronouns) (plist-get info :pronunciation))) " "))))
+ (org-entry-put (point) "PRESENT" (or (plist-get info :present) "?"))
+ (org-entry-put (point) "DURATION"
+ (or (plist-get info :video-duration)
+ (concat "~ " (plist-get info :duration))))
+ (org-entry-put (point) "BUFFER"
+ (format "%s (ending ~ %s)"
+ (plist-get info :buffer)
+ (format-time-string
+ "%l:%M%p"
+ (seconds-to-time
+ (+
+ (time-to-seconds
+ (org-timestamp-to-time (org-timestamp-split-range (org-timestamp-from-string (plist-get info :scheduled)))))
+ (* 60 (string-to-number (plist-get info :duration)))
+ (* 60 (string-to-number (plist-get info :buffer)))))
+ emacsconf-timezone)))
+ (org-entry-put (point) "Q_AND_A"
+ (or (plist-get info :bbb-room)
+ (plist-get info :q-and-a)
+ "?"))
+ ;(org-entry-put (point) "IRC"
+ ; (or (plist-get info :irc) "?"))
+ (org-entry-put (point) "CHECK_IN"
+ (or (plist-get info :check-in) "?"))
+ (org-entry-put (point) "EMAIL" (plist-get info :email))
+ (if (plist-get info :contact)
+ (org-entry-put (point) "CONTACT" (plist-get info :check-in)))
+ (if sort (emacsconf-upcoming-sort))))))
+
+
+
+
+
+
+(provide 'emacsconf-upcoming)
+;;; emacsconf-upcoming.el ends here
diff --git a/emacsconf.el b/emacsconf.el
index 8f2be80..357b271 100644
--- a/emacsconf.el
+++ b/emacsconf.el
@@ -75,5 +75,193 @@
"Return the newest file in PATH. Optionally filter by FILTER."
(car (sort (seq-remove #'file-directory-p (directory-files path 'full filter t)) #'file-newer-than-file-p)))
+
+(defun emacsconf-get-slug-from-string (search)
+ (if (listp search) (setq search (car search)))
+ (if (and search (string-match "\\(.*?\\) - " search))
+ (match-string 1 search)
+ search))
+(defun emacsconf-go-to-talk (&optional search)
+ (interactive (list (emacsconf-complete-talk)))
+ (when search
+ (setq search (emacsconf-get-slug-from-string search))
+ (pop-to-buffer (find-file-noselect emacsconf-org-file))
+ (let ((choices
+ (save-excursion
+ (delq nil
+ (org-map-entries
+ (lambda ()
+ (when (org-entry-get (point) "SLUG")
+ (cons
+ (concat (org-entry-get (point) "SLUG") " - "
+ (org-entry-get (point) "ITEM") " - "
+ (org-entry-get (point) "NAME") " - "
+ (org-entry-get (point) "EMAIL"))
+ (point)))))))))
+ (goto-char
+ (if search
+ (or (org-find-property "SLUG" search)
+ (cdr (seq-find (lambda (s) (string-match search (car s))) choices)))
+ (assoc-default (completing-read "Find: " choices)
+ choices)))
+ (org-reveal))))
+
+(defmacro emacsconf-with-talk-heading (search &rest body)
+ (declare (indent 1) (debug t))
+ `(progn
+ (emacsconf-go-to-talk ,search)
+ ,@body))
+
+(defun emacsconf-status-types ()
+ ;; TODO
+ )
+
+(defun emacsconf-get-talk-info-from-properties (o)
+ (let ((heading (org-heading-components))
+ (field-props '((:title "ITEM")
+ (:talk-id "TALK_ID")
+ (:slug "SLUG")
+ (:video-slug "VIDEO_SLUG")
+ (:public "PUBLIC")
+ (:qa-public "QA_PUBLIC")
+ (:scheduled "SCHEDULED")
+ (:uuid "UUID")
+ (:email "EMAIL")
+ (:caption-note "CAPTION_NOTE")
+ (:duration "TIME")
+ (:q-and-a "Q_AND_A")
+ (:bbb-room "ROOM")
+ (:irc "IRC")
+ (:intro-note "INTRO_NOTE")
+ (:check-in "CHECK_IN")
+ (:contact "CONTACT")
+ (:captioner "CAPTIONER")
+ (:youtube-url "YOUTUBE_URL")
+ (:toobnix-url "TOOBNIX_URL")
+ (:pronunciation "PRONUNCIATION")
+ (:pronouns "PRONOUNS")
+ (:buffer "BUFFER")
+ (:time "MIN_TIME")
+ (:present "PRESENT")
+ (:speakers "NAME")
+ (:speakers-short "NAME_SHORT")
+ (:video-file "VIDEO_FILE")
+ (:video-file-size "VIDEO_FILE_SIZE")
+ (:video-duration "VIDEO_DURATION")
+ (:alternate-apac "ALTERNATE_APAC")
+ (:extra-live-time "EXTRA_LIVE_TIME"))))
+ (apply
+ 'append
+ o
+ (list
+ :type (if (org-entry-get (point) "SLUG") 'talk 'headline)
+ :status (elt heading 2)
+ :level (car heading)
+ :url (concat emacsconf-base-url emacsconf-year "/talks/" (org-entry-get (point) "SLUG"))
+ :schedule-group
+ (org-entry-get-with-inheritance "SCHEDULE_GROUP")
+ :wiki-file-path (expand-file-name
+ (concat (org-entry-get (point) "SLUG") ".md")
+ (expand-file-name "captions" (expand-file-name emacsconf-year emacsconf-directory)))
+ :conf-year emacsconf-year
+ :start-time (when (org-entry-get (point) "SCHEDULED")
+ (org-timestamp-to-time
+ (org-timestamp-split-range
+ (org-timestamp-from-string
+ (org-entry-get (point) "SCHEDULED")))))
+ :end-time (when (org-entry-get (point) "SCHEDULED")
+ (org-timestamp-to-time
+ (org-timestamp-split-range
+ (org-timestamp-from-string
+ (org-entry-get (point) "SCHEDULED"))
+ t))))
+ (mapcar
+ (lambda (o) (list (car o) (org-entry-get (point) (cadr o))))
+ field-props))))
+
+(defun emacsconf-get-abstract-from-wiki (o)
+ (plist-put o :markdown (emacsconf-talk-markdown-from-wiki (plist-get o :slug))))
+
+(defun emacsconf-add-talk-status (o)
+ (plist-put o :status-label
+ (assoc-default (plist-get o :status)
+ (emacsconf-status-types) 'string= "")))
+
+(defvar emacsconf-talk-info-functions '(emacsconf-get-talk-info-from-properties emacsconf-add-talk-status))
+
+(defun emacsconf-get-talk-info-for-subtree ()
+ (seq-reduce (lambda (prev val) (funcall val prev))
+ emacsconf-talk-info-functions
+ nil))
+
+(defun emacsconf-get-talk-info (&optional description-source)
+ (with-current-buffer (find-file-noselect emacsconf-org-file)
+ (save-excursion
+ (let (talk results (status-types (emacsconf-status-types)))
+ (org-map-entries
+ (lambda ()
+ (when (or (org-entry-get (point) "SLUG")
+ (org-entry-get (point) "INCLUDE_IN_INFO"))
+ (setq results
+ (cons (emacsconf-get-talk-info-for-subtree)
+ results)))))
+ (nreverse results)))))
+
+(defun emacsconf-filter-talks (list)
+ "Return only talk info in LIST."
+ (seq-filter
+ (lambda (talk) (eq (plist-get talk :type) 'talk))
+ list))
+
+(defun emacsconf-get-talk-info-from-file (&optional filename)
+ (with-temp-buffer
+ (insert-file-contents (or filename "conf.org"))
+ (org-mode)
+ (org-show-all)
+ (goto-char (point-min))
+ (goto-char (org-find-property "ID" "talks"))
+ (emacsconf-get-talk-info 'wiki)))
+
+
+(defun emacsconf-find-talk-info (filter &optional info)
+ (setq info (or info (emacsconf-filter-talks (emacsconf-get-talk-info))))
+ (when (stringp filter) (setq filter (list filter)))
+ (or (seq-find (lambda (o) (string= (plist-get o :slug) (car filter))) info)
+ (seq-find (lambda (o)
+ (let ((case-fold-search t)
+ (all (mapconcat (lambda (f) (plist-get o f)) '(:title :speakers :slug) " ")))
+ (null (seq-contains-p
+ (mapcar (lambda (condition) (string-match condition all)) filter)
+ nil))))
+ info)))
+
+(defun emacsconf-goto-talk-id (id)
+ (goto-char (org-find-property "TALK_ID" id)))
+
+(defun emacsconf-talk-markdown-from-wiki (slug)
+ "Return the markdown from SLUG."
+ (when (file-exists-p (expand-file-name (format "%s/talks/%s.md" emacsconf-year slug) emacsconf-directory))
+ (with-temp-buffer
+ (insert-file-contents (expand-file-name (format "%s/talks/%s.md" emacsconf-year slug) emacsconf-directory))
+ (goto-char (point-min))
+ (while (re-search-forward "<!--" nil t)
+ (let ((start (match-beginning 0)))
+ (when (re-search-forward "-->" nil t)
+ (delete-region start (match-end 0)))))
+ (goto-char (point-min))
+ (while (re-search-forward "\\[\\[![^]]+\\]\\]" nil t)
+ (replace-match ""))
+ (string-trim (buffer-string)))))
+
+(defun emacsconf-replace-plist-in-string (attrs string)
+ "Replace ${keyword} from ATTRS in STRING."
+ (let ((a attrs))
+ (while a
+ (setq string
+ (replace-regexp-in-string (regexp-quote (concat "${" (substring (symbol-name (pop a)) 1) "}"))
+ (pop a)
+ string t t)))
+ string))
+
(provide 'emacsconf)
;;; emacsconf.el ends here