summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--emacsconf-mail.el83
-rw-r--r--emacsconf-schedule.el65
-rw-r--r--emacsconf.el24
3 files changed, 114 insertions, 58 deletions
diff --git a/emacsconf-mail.el b/emacsconf-mail.el
index b0f5aa6..ff65868 100644
--- a/emacsconf-mail.el
+++ b/emacsconf-mail.el
@@ -156,6 +156,11 @@ Group by e-mail."
(defun emacsconf-mail-group-by-email (info)
(seq-group-by (lambda (o) (plist-get o :email)) info))
+(defun emacsconf-mail-speaker-from-slug (talk)
+ "E-mail the speaker for TALK."
+ (interactive (list (emacsconf-complete-talk-info)))
+ (compose-mail (plist-get talk :email)))
+
(defun emacsconf-mail-speaker (&optional subject body talk)
"Compose a message to the speaker of the current talk."
(interactive (list nil nil (emacsconf-complete-talk-info)))
@@ -247,39 +252,54 @@ Group by e-mail."
(defun emacsconf-mail-parse-submission (body)
"Extract data from EmacsConf 2023 submissions in BODY."
(when (listp body) (setq body (plist-get (car body) :content)))
- (let ((data (list :body body))
- (fields '((:title "^[* ]*Talk title")
- (:description "^[* ]*Talk description")
- (:format "^[* ]*Format")
- (:intro "^[* ]*Introduction for you and your talk")
- (:name "^[* ]*Speaker name")
- (:availability "^[* ]*Speaker availability")
- (:q-and-a "^[* ]*Preferred Q&A approach")
- (:public "^[* ]*Public contact information")
- (:private "^[* ]*Private emergency contact information")
- (:release "^[* ]*Please include this speaker release"))))
+ (let* ((data (list :body body))
+ (fields '((:title "^[* ]*Talk title")
+ (:description "^[* ]*Talk description")
+ (:format "^[* ]*Format")
+ (:intro "^[* ]*Introduction for you and your talk")
+ (:name "^[* ]*Speaker name")
+ (:availability "^[* ]*Speaker availability")
+ (:q-and-a "^[* ]*Preferred Q&A approach")
+ (:public "^[* ]*Public contact information")
+ (:private "^[* ]*Private emergency contact information")
+ (:release "^[* ]*Please include this speaker release")))
+ field
+ (field-regexp (mapconcat
+ (lambda (o)
+ (concat "\\(?:" (cadr o) "\\)"))
+ fields "\\|")))
(with-temp-buffer
(insert body)
(goto-char (point-min))
;; Try to parse it
- (while fields
- ;; skip the field title
- (when (and (or (looking-at (cadar fields))
- (re-search-forward (cadar fields) nil t))
- (re-search-forward "\\(:[ \t\n]+\\|\n\n\\)" nil t))
- ;; get the text between this and the next field
- (setq data (plist-put data (caar fields)
- (buffer-substring (point)
- (or
- (when (and (cdr fields)
- (re-search-forward (cadr (cadr fields)) nil t))
- (goto-char (match-beginning 0))
- (point))
- (point-max))))))
- (setq fields (cdr fields)))
+ (catch 'done
+ (while (not (eobp))
+ ;; skip the field title
+ (unless (looking-at field-regexp)
+ (unless (re-search-forward field-regexp nil t)
+ (throw 'done nil)))
+ (goto-char (match-beginning 0))
+ (setq field (seq-find (lambda (o)
+ (looking-at (cadr o)))
+ fields))
+ (when field
+ ;; get the text between this and the next field
+ (re-search-forward "\\(:[ \t\n]+\\|\n\n\\)" nil t)
+ (setq data
+ (plist-put
+ data
+ (car field)
+ (buffer-substring
+ (point)
+ (or (and
+ (re-search-forward field-regexp nil t)
+ (goto-char (match-beginning 0))
+ (point))
+ (point-max))))))))
(if (string-match "[0-9]+" (or (plist-get data :format) ""))
(plist-put data :time (match-string 0 (or (plist-get data :format) ""))))
- data)))
+ data)
+))
;;;###autoload
(defun emacsconf-mail-review ()
@@ -293,9 +313,9 @@ Group by e-mail."
(message-goto-body)
(save-excursion
(insert (format
- "Thanks for submitting your proposal! (TODO: feedback) We're experimenting
-with early acceptance this year, so we'll wait a week (~ %s) in case the
-other volunteers want to chime in regarding your talk. =)
+ "Thanks for submitting your proposal! (TODO: feedback)
+
+We'll wait a week (~ %s) in case the other volunteers want to chime in regarding your talk. =)
"
notification-date)))))
@@ -470,7 +490,8 @@ Include some other things, too, such as emacsconf-year, title, name, email, url,
(format "from:%s or to:%s" o o))
(split-string (plist-get talk :email) " *, *")
" or ")
- " or (" emacsconf-id " and " (plist-get talk :slug) ")")))
+ ;; " or (" emacsconf-id " and " (plist-get talk :slug) ")"
+ )))
;;; Volunteers
diff --git a/emacsconf-schedule.el b/emacsconf-schedule.el
index 0cc261f..30cb110 100644
--- a/emacsconf-schedule.el
+++ b/emacsconf-schedule.el
@@ -365,11 +365,14 @@ Pairs with `emacsconf-schedule-dump-sexp'."
(when track
(dom-set-attribute node 'fill (plist-get track :color)))))
-(defun emacsconf-schedule-svg-color-by-availability (o node &optional parent)
- (dom-set-attribute node 'fill
- (if (string-match "^[><]" (plist-get o :availability))
- "gray"
- "green")))
+(defun emacsconf-schedule-svg-color-by-availability (o node &optional _)
+ (dom-set-attribute node 'fill
+ (cond
+ ((string-match "^<" (or (plist-get o :availability) ""))
+ "lightblue")
+ ((string-match "^>" (or (plist-get o :availability) ""))
+ "peachpuff")
+ (t "gray"))))
(defun emacsconf-schedule-svg (width height &optional info)
"Make the schedule SVG for INFO."
@@ -406,12 +409,14 @@ Other status: gray"
"PROCESSING"
"TO_AUTOCAP"))
"palegoldenrod")
- ((rx (or "TO_ASSIGN"))
+ ("TO_ASSIGN"
"yellow")
- ((rx (or "TO_CAPTION"))
+ ("TO_CAPTION"
"lightgreen")
- ((rx (or "TO_STREAM"))
+ ("TO_STREAM"
"green")
+ ("TODO"
+ "lightgray")
(_ "gray")))))
(defun emacsconf-schedule-svg-days (width height days)
@@ -570,7 +575,27 @@ Talks with a FIXED_TIME property are not moved."
(setq end-time (time-add (org-get-scheduled-time (point)) (seconds-to-time duration)))
(setq current-time (time-add end-time (* (string-to-number (or (plist-get talk :buffer) "0")) 60))))))))))))
-(defun emacsconf-schedule-validate-time-constraints (&optional info)
+(defun emacsconf-schedule-get-key-func ()
+ "Get the sorting key for the current entry."
+ (org-entry-get (point) "SLUG"))
+(defun emacsconf-schedule-sort-compare-func (a b)
+ (let* ((entry-a (emacsconf-resolve-talk a emacsconf-schedule-draft))
+ (entry-b (emacsconf-resolve-talk b emacsconf-schedule-draft))
+ (track-index-a (or (seq-position emacsconf-tracks (emacsconf-get-track (plist-get entry-a :track))) 0))
+ (track-index-b (or (seq-position emacsconf-tracks (emacsconf-get-track (plist-get entry-b :track))) 0)))
+ (cond
+ ((string= (plist-get entry-a :status) "CANCELLED") nil)
+ ((string= (plist-get entry-b :status) "CANCELLED") t)
+ ((< track-index-a track-index-b) t)
+ ((> track-index-a track-index-b) nil)
+ ((string< (plist-get entry-a :scheduled)
+ (plist-get entry-b :scheduled)) t)
+ (t nil))))
+(defun emacsconf-schedule-sort-entries ()
+ (interactive)
+ (org-sort-entries nil ?f #'emacsconf-schedule-get-key-func #'emacsconf-schedule-sort-compare-func))
+
+(defun emacsconf-schedule-validate-time-constraints (info &rest _)
(interactive)
(let* ((info (or info (emacsconf-get-talk-info)))
(results (delq nil
@@ -588,9 +613,9 @@ Talks with a FIXED_TIME property are not moved."
(constraint (emacsconf-schedule-get-time-constraint o)))
(when constraint
(setq result (apply #'emacsconf-schedule-check-time
- (plist-get o :slug)
- o
- constraint))
+ (plist-get o :slug)
+ o
+ constraint))
(when result (plist-put o :invalid result))
result)))
info)))))
@@ -640,7 +665,7 @@ Both start and end time are tested."
(when diff
(list (concat "Missing talks: " (string-join diff ", "))))))
-(defun emacsconf-schedule-validate-no-duplicates (sched)
+(defun emacsconf-schedule-validate-no-duplicates (sched &optional info)
(let* ((sched-slugs (mapcar (lambda (o) (plist-get o :slug))
(emacsconf-filter-talks sched)))
(dupes (seq-filter (lambda (o) (> (length (cdr o)) 1))
@@ -648,12 +673,14 @@ Both start and end time are tested."
(when dupes
(list (concat "Duplicate talks: " (mapconcat 'car dupes ", "))))))
+(defvar emacsconf-schedule-validation-functions '(emacsconf-schedule-validate-time-constraints
+ emacsconf-schedule-validate-live-q-and-a-sessions-are-staggered
+ emacsconf-schedule-validate-all-talks-present
+ emacsconf-schedule-validate-no-duplicates))
(defun emacsconf-schedule-validate (sched &optional info)
- (append
- (emacsconf-schedule-validate-time-constraints sched)
- (emacsconf-schedule-validate-live-q-and-a-sessions-are-staggered sched)
- (emacsconf-schedule-validate-all-talks-present sched info)
- (emacsconf-schedule-validate-no-duplicates sched)))
+ (seq-mapcat (lambda (func)
+ (funcall func sched info))
+ emacsconf-schedule-validation-functions))
(defun emacsconf-schedule-inflate-tracks (tracks schedule)
(mapcar
@@ -741,7 +768,7 @@ If emacsconf-schedule-apply is non-nil, update `emacsconf-org-file' and the wiki
(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)
+(defun emacsconf-schedule-validate-live-q-and-a-sessions-are-staggered (schedule &rest _)
"Try to avoid overlapping the start of live Q&A sessions.
Return nil if there are no errors."
(when emacsconf-schedule-validate-live-q-and-a-sessions-buffer
diff --git a/emacsconf.el b/emacsconf.el
index b73e816..f506626 100644
--- a/emacsconf.el
+++ b/emacsconf.el
@@ -280,14 +280,15 @@
(emacsconf-get-slug-from-string (emacsconf-complete-talk)))
(defun emacsconf-export-slug (link description format _)
- (let ((path (format "https://emacsconf.org/%s/talks/%s" emacsconf-year link))
- (desc (or description link)))
+ (let* ((path (format "https://emacsconf.org/%s/talks/%s" emacsconf-year link))
+ (talk (emacsconf-resolve-talk link))
+ (desc (or description link)))
(pcase format
(`html
- (format "<a href=\"#%s\">%s</a>" link desc))
+ (format "<a href=\"#%s\" title=\"%s\">%s</a>" link (plist-get talk :title) desc))
(`ascii (format "%s (%s)" desc path))
- (`markdown
- (format "[[%s|%s/talks/%s]]" desc emacsconf-year link))
+ (`md
+ (format "[%s](%s \"%s\")" desc path (plist-get talk :title)))
(_ path))))
(with-eval-after-load 'org
@@ -812,9 +813,9 @@ If INFO is specified, limit it to that list."
prev)))
;; (emacsconf-previous-talk (emacsconf-resolve-talk "lspbridge"))
-(defun emacsconf-resolve-talk (talk)
+(defun emacsconf-resolve-talk (talk &optional info)
"Return the plist for TALK."
- (if (stringp talk) (emacsconf-find-talk-info talk) talk))
+ (if (stringp talk) (emacsconf-find-talk-info talk info) talk))
(defun emacsconf-find-talk-info (filter &optional info)
(setq info (or info (emacsconf-filter-talks (emacsconf-get-talk-info))))
@@ -921,7 +922,7 @@ If INFO is specified, limit it to that list."
"u" #'emacsconf-update-talk
"t" #'emacsconf-insert-talk-title
"m" #'emacsconf-mail-speaker-from-slug
- "n" #'emacsconf-notmuch-search-mail-from-entry
+ "n" #'emacsconf-mail-notmuch-search-for-talk
"f" #'org-forward-heading-same-level
"b" #'org-backward-heading-same-level
"RET" #'emacsconf-go-to-talk)
@@ -1556,5 +1557,12 @@ tracks with the ID in the cdr of that list."
:complete #'emacsconf-ansible-complete
:export #'emacsconf-ansible-export
:follow #'emacsconf-ansible-open))
+
+(defun emacsconf-end-of-week (date)
+ "Useful for analyzing data. Assumes week ends Sunday."
+ (let ((d (decode-time (date-to-time date))))
+ (format-time-string "%Y-%m-%d"
+ (encode-time
+ (decoded-time-add d (make-decoded-time :day (% (- 7 (decoded-time-weekday d)) 7)))))))
(provide 'emacsconf)
;;; emacsconf.el ends here