emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Rodrigo Morales <me@rodrigomorales.site>
To: <emacs-orgmode@gnu.org>
Subject: Re: Elisp function to get the upcoming event with the closest scheduled date
Date: Wed, 3 Jan 2024 00:29:49 -0500	[thread overview]
Message-ID: <87sf3f56ia.fsf@rodrigomorales.site> (raw)
In-Reply-To: <355194690966e37531b8643292fe2b86@rodrigomorales.site>

Rodrigo Morales <me@rodrigomorales.site> writes:

I managed to show the entry with the nearest upcoming scheduled date in
the tab bar with the following utilities (see code block below). Here's
an screenshot:
https://upload.wikimedia.org/wikipedia/commons/f/f3/Show_information_of_Org_Agenda_in_tab-bar.png

Here are some notes of the code:

+ The function my/org-agenda-nearest-upcoming-scheduled-search performs
  linear search through all headings in all files listed in
  org-agenda-files in order to search the entry with the nearest
  upcoming scheduled date.
+ The variable my/org-agenda-nearest-upcoming-scheduled stores the
  information of the event.
+ I added my/org-agenda-nearest-upcoming-scheduled-display to
  tab-bar-format in order to display the information that I requested.

Because my/org-agenda-nearest-upcoming-scheduled-search uses linear
search it is a rather expensive operation if there are many entries to
look up. I haven't decided yet when this functino should be called so
that it is called the fewer amount of times and it actually shows th
entry with the nearest upcoming scheduled date. It is necessary that it
is called in the proper times so that the
my/org-agenda-nearest-upcoming-scheduled is updated accordingly when
setting the scheduled date for new entries.

#+BEGIN_SRC elisp
(defvar my/org-agenda-nearest-upcoming-scheduled nil
  "Store entry with the nearest upcoming scheduled date.

The value should store a CONS CELL whose CAR is the title of the
entry and the CDR is the date of the entry.")

(defun my/org-agenda-nearest-upcoming-scheduled-search ()
  "Set the value for variable that store the entry with the nearest upcoming scheduled date."
  (let (nearest-entry
        ;; Store the scheduled date in seconds of the current entry.
        entry-scheduled
        (current-time (float-time)))
    ;; Iterate through all files in `org-agenda-files'
    (dolist (file org-agenda-files)
      (with-current-buffer (find-file-noselect file)
        ;; Iterate through all headings
        (org-map-entries
         (lambda ()
           (when (and
                  ;; Check the current entry has SCHEDULED
                  (setq entry-scheduled (org-entry-get nil "SCHEDULED"))
                  ;; If the currenty entry has SCHEDULED, we proceed
                  ;; to convert it to seconds using org-2ft. We cannot
                  ;; write org-entry-get and org-2ft in the same
                  ;; expression because it is possible that
                  ;; org-entry-get returns nil and org-2ft returns 0
                  ;; when nil is passed, so we won't be checking that
                  ;; the entry has SCHEDULED.
                  (setq entry-scheduled (org-2ft entry-scheduled))
                  ;; Check time of the entry is greater than the current time
                  (> entry-scheduled current-time))
             ;; If there's no nearest entry yet, then store the
             ;; current entry as the nearest entry.
             ;;
             ;; We use an if conditional because for plist-put to
             ;; work, it is needed that the property list is not nil.
             (if (null nearest-entry)
                 (setq nearest-entry
                       `(:item ,(org-entry-get nil "ITEM")
                         :scheduled ,entry-scheduled
                         :scheduled-string ,(org-entry-get nil "SCHEDULED")
                         :marker ,(point-marker)))
               ;; If a nearest entry has been found previously,
               ;; compare their scheduled time.
               (when (> (plist-get nearest-entry :scheduled) entry-scheduled)
                 (plist-put nearest-entry :item (org-entry-get nil "ITEM"))
                 (plist-put nearest-entry :scheduled entry-scheduled)
                 (plist-put nearest-entry :scheduled-string (org-entry-get nil "SCHEDULED"))
                 (plist-put nearest-entry :marker (point-marker)))))))))
    (setq my/org-agenda-nearest-upcoming-scheduled nearest-entry)
    (unless nearest-entry
      (error "No event with scheduled date than the current date was found."))))

(defun my/org-agenda-nearest-upcoming-scheduled-display ()
  (if my/org-agenda-nearest-upcoming-scheduled
      (progn
        ;; If the previously found entry has an scheduled date that has
        ;; already passed, let's search for a new upcoming scheduled.
        (when (<
               (plist-get my/org-agenda-nearest-upcoming-scheduled :scheduled)
               (float-time))
          (my/org-agenda-nearest-upcoming-scheduled-search))
        (let* ((time-difference
                (truncate
                 (-
                  (plist-get my/org-agenda-nearest-upcoming-scheduled :scheduled)
                  (float-time))))
               (days (/ time-difference 86400))
               (hours (/ (% time-difference 86400) 3600))
               (minutes (/ (% time-difference 3600) 60)))
          ;; TODO: The following is a list of menu items. I still
          ;; don't know the meaning of the first item in the menu item
          ;; definition, so I've written foo.
          `((foo
             menu-item
             ,(propertize
               (concat
                (plist-get my/org-agenda-nearest-upcoming-scheduled :item)
                " | "
                (string-join
                 (delq
                  nil
                  (funcall
                   'append
                   (unless (eq days 0) (list (number-to-string days) "days"))
                   (unless (eq hours 0) (list (number-to-string hours) "hours"))
                   (unless (eq minutes 0) (list (number-to-string minutes) "minutes"))))
                 " "))
               'face
               `(:background ,(face-attribute 'default :background)
                 :foreground ,(face-attribute 'default :foreground)
                 :box (:line-width (1 . 1)
                       :color ,(face-attribute 'default :foreground))))
             my/org-agenda-nearest-upcoming-scheduled-jump
             :help (plist-get my/org-agenda-nearest-upcoming-scheduled :scheduled-string)))))
    `((foo
       menu-item
       ,(propertize
         "No entry found"
         'face
         `(:background ,(face-attribute 'default :background)
           :foreground ,(face-attribute 'default :foreground)
           :box (:line-width (1 . 1)
                 :color ,(face-attribute 'default :foreground))))
       ignore
       :help "No entry with the nearest upcoming scheduled date found."))))

(defun my/org-agenda-nearest-upcoming-scheduled-jump ()
  (interactive)
  (with-current-buffer (switch-to-buffer
                        (marker-buffer
                         (plist-get my/org-agenda-nearest-upcoming-scheduled :marker)))
    (goto-char (plist-get my/org-agenda-nearest-upcoming-scheduled :marker))))
#+END_SRC



      reply	other threads:[~2024-01-03  5:30 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-12-31 17:30 Elisp function to get the upcoming event with the closest scheduled date Rodrigo Morales
2024-01-03  5:29 ` Rodrigo Morales [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87sf3f56ia.fsf@rodrigomorales.site \
    --to=me@rodrigomorales.site \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).