Reuse date prompts values in a capture templates

I am trying to write a custom accessor (maybe not the right label) for an in-buffer keyword.

I have a few accessors for custom properties that work fine like the following for example:

  (cl-defmethod org-roam-node-my-namespace ((node org-roam-node))
    "Return the namespace for NODE."
    (cdr (assoc-string "NAMESPACE" (org-roam-node-properties node))))

I can understand from that code and the doc that properties are treated as a list of KEY VALUE strings, and that list apparently does not include in-buffer keywords/properties (not sure which is the right term).

I have a keyword set in-buffer called #+logstamp: which is a timestamp and I am trying to achieve the same as above. I found the org-roam--get-keyword function that seems to do what I want using some regex to extract in buffer keywords.

From the function’s help it looks like all I need to provide as an argument is NAME which I interpret as the KEY part, and I expect the VALUE in return. So I tried the following:

  (cl-defmethod org-roam-node-my-logstamp ((node org-roam-node))
    "Return the logstamp for NODE."
    (org-roam--get-keyword "logstamp"))

Trying to add it to an org-roam-display-template, it does not seem to work at all. Does anyone know if what I am trying to achieve is possible, and if yes, am I going in the right direction at all ?

Thanks in advance for your time.

Its because the display template is formatted according the values available in the node struct

If you add it in the PROPERTIES - i can help you achieve it. Otherwise we would have to visit the buffer to get the value and it wont be a good experience

Thanks for your input.

If I understand you correctly, the ‘in-buffer keywords’ are not stored in the org-roam database to begin with…

Reading the org-roam--get-keyword info again, I can see it is clearly stated that it is meant to retrieve thos values in current BUFFER and not in a NODE.

So I guess I have to find another way. Thank you for offering to help me further, but I have been able to retrieve values from the properties drawer with the first snippet in my original post.

My problem is that I need the timestamp to be visible as well when reading/editing the note, and I do not want to be prompted twice for the same date in the capture-template. So far I have not been able to make the %\N template expansion element work with timestamps in order to re-use a value obtained from a date prompt.

Not sure how to go about it…

Can you provide your capture template - I can try to see if its possible. If you provide the value in the capture template itself – it should be possible to replicate the data. I can atleast try, if not anything else.

Thanks a lot for being willing to try. Here is a simplified version of my ‘log’ template:

         ("l" "Create a log" plain "%?"
          :target (file+head "logs/%<%Y%m%d%H%M%S>-${slug}.org" "
:ID:            ${id}
:LOGSTAMP:      %^{Log Date}t
#+title: ${title}

- Log Date: %\\1 \n")

I always get a type error suggesting the template mechanism is expecting a string. I tried to format the %\\1 with a %(format-time-string) wrapper hoping to provide the correct format and get a more readable string at the same time, but it threw an error as well.

I had asked about this date prompt issue as a separate question on stackexchange, and just received an interesting answer.

According to a user whowas able to review the org-capture code, the %\N mechanism is only designed to work with string prompts; not properties, dates or any other type of prompts.

@akashp I am giving you the heads up so you do not waste too much time on this. As it was suggested to me I have subscribed to the org-mode mailing list to submit a resquest feature, but I am afraid it might take a while as my subscription request is still under review.

I think I will try to find a workaround like using a variable/function to achieve this result. This was actually the topic of my first question here, but I could not make it work in spite of all Nobiot’s efforts to help me out, and I settled then for the %\N solution which was good enough for me at the time since I was dealing with string prompts exclusively.

Thanks again for helping out, I appreciate it.

(add-to-list 'org-roam-capture-templates
	     '("l" "Create a log" plain "%?"
               :target (file+head "logs/%<%Y%m%d%H%M%S>-${slug}.org" "
:LOGSTAMP:      %T
#+title: ${title}

- Log Date: %T \n")

If you want just the current time why dont you use the %T? Or do you want to enter some custom value ? Also you dont have to supply the ID – it would be automatically taken - its redundant here.

@lyndhurst An alternative idea is to use date/time stamp as an ID. Discussed with different examples with @akashp and others in this thread.

Mine like this:
:id: 2021-04-05T144104
File name is constructed like this ${id}_${slug}.md (I use markdown files).

I had never tried omitting the {id} field; I figured since I am manually adding the property drawer I should be in charge of the id too :slight_smile: Thanks for the tip.

Yes this template is designed to record events no matter when they occured which allows me to then build timelines on specific topics. This is not a real time log in the computer sense of it…

Thanks for pitching in. This would of course be perfectly valid as well like using %T for a ‘now’ timestamp, but I do need to enter a custom value in that field which can be a date or a date and time type of data.

@lyndhurst You dont have to wait for org mode developers to do this

change this

 ;; -----------------------------------------------------------------------------------
		       ;; (org-insert-time-stamp
		       ;; 	time (or org-time-was-given upcase?)
		       ;; 	(member key '("u" "U"))
		       ;; 	nil nil (list org-end-time-was-given))

		       ;; Insert the time stamp and capture its string representation
		       (let ((timestamp (org-insert-time-stamp
					 time (or org-time-was-given upcase?)
					 (member key '("u" "U"))
					 nil nil (list org-end-time-was-given))))
			 ;; Push the string representation of the timestamp to the strings variable
			 (push (format "%s" timestamp) strings))))
		    ;; -----------------------------------------------------------------------------------------

can you find this place inside the #'org-capture-fill-template? Make this modification and youre good to go, the function is quite byzantine. The file in the github repo was last updated 12 years ago the function has changed since then – you should find the commented out region – its towards the end and make the modification.

If you are unable to do this - ping me – I will make the modification and send it – but since the function is too long - it would be better if you could make the modification yourself.

I hope you know how to :override a function. If you are brave you can make the modification in the source itself and eval-buffer but it to be compiled ready youd have to restart twice.


Adding some screenshots to show it is working for me

Thanks a lot for all this work. I have to admit this is a lot for me, I have only been using org mode for a few weeks, and I am on Doom Emacs.

I do not know about :override, but I have already copied some functions from packages code source to my config file to make minor modifications.

If this is the same type of situation, with all the pointers you gave me, I should be able to do it myself. About modifying the source file itself, I am afraid it would be overwritten on Doom update or lost during re-install, my config is at least versioned and backed up…

Anyway, I will take some time to look through it later tonight (and learn at the same time), and I will report back on my progress. Knowing that it works beforehand is a good motivation too :slight_smile: thanks again.

1 Like

There was no problem for me to locate the org-capture-fill-template function, and the org-insert-time-stamp bit within it.

I copied the entire function to my config, and enclosed it in a (after! org ..) statement since I am on Doom, and it is usually required to override functions loaded by a package.

Then I pasted the modification snippet you gave me over the original org-insert-time-stamp part. With a little more context it looks like that:

	    ;; These are the date/time related ones
	    (setq org-time-was-given (equal (upcase char) char))
	    (setq time (org-read-date (equal (upcase char) char) t nil
	    (if (equal (upcase char) char) (setq org-time-was-given t))
		       ;; (org-insert-time-stamp
		       ;; 	time (or org-time-was-given upcase?)
		       ;; 	(member key '("u" "U"))
		       ;; 	nil nil (list org-end-time-was-given))

		       ;; Insert the time stamp and capture its string representation
		       (let ((timestamp (org-insert-time-stamp
					 time (or org-time-was-given upcase?)
					 (member key '("u" "U"))
					 nil nil (list org-end-time-was-given))))
			 ;; Push the string representation of the timestamp to the strings variable
			 (push (format "%s" timestamp) strings)))

On the last line, I removed one closing parenthesis so the last one closes the first one in the snippet I have pasted. Leaving your code as is, Doom was throwing a warning about that.

I must have done something wrong because when I try using a template, I get the following error: progn: Symbol’s function definition is void: org-capture-steal-local-variables.

Dont seem to me the correct place

Check line number inside the function with display-line-number-mode

Why are these variables defined with setq in your case here? whats your org version?

WAIT !!!


Do M-x describe-symbol, I just gave it to you to show the relative position to help you find it in your version of emacs.

Heres a more current one – but see the problem with their git is that you cant quote multiple lines at once – so I gave you the one I found first while doing a google search from github.

:rofl: That was pretty dumb of me !

I noticed when you gave me the first link you mentioned it was 12 years old, and I thought to myself ‘Damn orgmode is pretty future proof, we are still using code that is 12 years old !’

Anyway, I got it from the describe-symbol and it works very well now. I cannot thank you enough you saved me weeks of on & off headaches trying to solve that one.

I can even link to this thread when I submit a feature request to the mailing list if they ever accept my request. I will check tomorrow.

Thanks again.

1 Like

What about writing a function that takes a node-id and a property name.
If the buffer is open, switch to it, retrieve the property value. If not open, visit it, do it, dispose it. org-roam-node-visit will visit the buffer (save excursion while doing all this).

if you call this function sporadically it will not be onerous.

Thanks for sharing your thoughts, but in my particular use case I think that would be onerous indeed. I need this property to add to a custom org-roam-node-find template display, and also in org-roam-ql dynamic blocks to build timelines.

I have not had time to work more on my templates yet, but I think Akashp solution was as good as it could get. Except for the fact that I will not get updates to my org-capture.el anymore…

If you are concerned about overriding the built-in function, here is yet another alternative. It works on my end:

The idea is to add the LOGSTAMP list item in the body separately when Emacs prepares the capture buffer.

(add-hook 'org-roam-capture-new-node-hook #'+org-roam-capture-insert-logdate)

(defun +org-roam-capture-insert-logdate ()
  (when-let (p (org-entry-get (point) "LOGSTAMP"))
    (org-roam-end-of-meta-data t)
    (insert (concat "\n- " p "\n"))))

(add-to-list 'org-roam-capture-templates
             '("l" "Create a log" plain "%?"
              :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "
:LOGSTAMP:      %^{Log Date}t
#+title: ${title}

I think @akashp 's code change is good so it would be good if you could report it as a patch suggestion upstream…

1 Like

I totally agree with you, I subscribed to the mailing list yesterday to post a feature request, but I still did not get an email back from them. I will check back later today.

Thanks, I will try it a bit later, I am scratching my head on yet another org customization at the moment (one of the last on my list before I can use it full time so this is good). I am a little worried something will break at some point if a package gets an update this is true. I have already overidden several functions this way and it feels a bit fragile.