AppleScript problems with dates and custom metadata

You may do better to ignore the previous pleas. There is a lot more work needed as I now understand. The DT links are not, of course, that simple as they use the record UUIDs. Let me not waste everyone’s time and do as I should first have done — which is to spend rather more time researching the issue!

All I really wanted to do was to speed up the process of adding links to other DT records by using the date of those records but I suspect that’s not going to be easy.

Stephen

I have a short JavaScript example

(() => {
  const app = Application("DEVONthink 3");
  if (app.selectedRecords.length === 0) {
    return;
  }
  const rec = app.selectedRecords[0];
  app.includeStandardAdditions = true; 
  
  /* Ask for user input, use current date as default */
  const today = new Date();
  const todayString = [today.getDate(), today.getMonth()+1, today.getFullYear()].join('-');
  userInput = app.displayNameEditor("Enter date", {defaultAnswer: todayString});

  /* Assume that the user entered "dd/mm/yy[yy]" with an arbitrary delimiter */
  const dateParts = userInput.split(/[-.,/]/).reverse(); /* ISO date */
  /* dateParts: [yyyy, mm, dd]; */
  const linkDate = new Date(...dateParts);   /* create Date object from user input */

  const weekday = new Intl.DateTimeFormat('en-US', {weekday: 'long'}).format(linkDate);
  const linkString = `${weekday} ${dateParts.reverse().join(' ')}.md`;

  app.addCustomMetaData(linkString, {to: rec, for: "link"});
  
})()

that might do what you want. However

Yes. Entering a date as a free-form text is risky.

It might be easier to help you if you could provide some mockup of what you want to see/achieve. I have difficulties extracting that from the text you wrote, not being a native english speaker …

Thanks so much for the response. Let me try to explain (even though I suspect what I need to do is probably not achievable!).

Take part of a markdown entry (which I’ll call “the Current Record”) which looks like this:

using the first of our [cheaper, compatible filters](dayone://view?entryId=4AE3F83A960544B085A216AABE01A6CA)—and used the first

and which displays like this:

using the first of our cheaper, compatible filters—and used the first

I can click on the link in DT and it takes me to the relevant entry in Day One, of course (in this case, one dated Monday 31 May 2021 and named “Monday 31 May 2021.md”).

What I was trying to do was to find a (relatively) quick and easy way of retrievibng from DevonThink the (as I now understand it) UUID of the 31 May 2021 record and inserting that as a custom metadata link in Current Record.

The problem is that I probably have thousands of records that contain Day One links so unless I can somehow automate the process I’ve outlined I don’t really think it’s worth tackling at all! I guess it was the challenge that initially attracted me.

Sorry if the original post was not so clear as it might have been.

Stephen

I should add, to make it clear, that I’m clicking on the Day One link in order, manually, to discover the date to which it refers and it’s after that I’d like to automate the process to the extent it may be possible.

Please don’t spend a lot of time of this. It’s a sort of fantasy project which may never come to fruition!

Stephen

OK, I think I got it. Your files are named “Weekday d Monthname y” in DT. They’re named something else in DO (or rather: there are no filenames there). You want to augment the custom meta data in DT so that all links to DO are shown as x-devonthink URLs in the “link” field (as a side node: That’s ok if you have only one link in a file, otherwise … well).

So you need the date of the DO note the dayone link is pointing to. As far as I can tell, this step can not be automatized. In fact, there’s hardly any automation support at all in DO: they offer a CLI with exactly one command, which is a bit poor. No scripting at all. Understandable, given that they want you to use their software, not extract your data from it and use it somewhere else (I tend to be opionated).

Now, DO stores its data in the file ~/Library/Group Containers/5U8NS4GX82.dayoneapp2/Data/Documents/DayOne.sqlite. That is, surprise, a SQLite database. As such, it can be queried with a shell command like so

sqlite3 DayOne.sqlite 'select * from zentry where zuuid="E3470F309F7A425184D6A2F45E72302F"'

and it will utter something like this:

2|12|2|0|0|0|21|7|2021|1|0|0||1|||||||648511200|25.8850209712982|648649823.148132|648518400|268F3C7E-528E-49CD-8B7C-BDBC0C5B803B|MBP-ck|MacBookPro16,1|MacBook Pro|macOS|11.5|||10|1:2021:07|# Das war der 21\.7\.21

An dem auch nix passiert ist\.|||{"meta":{"version":1,"small-lines-removed":true,"created":{"platform":"com.bloombuilt.dayone-mac","version":1300}},"contents":[{"attributes":{"line":{"identifier":"46C5D5EE-4C51-4308-8746-6BB96187B87A","header":1}},"text":"Das war der 21.7.21\n"},{"text":"\nAn dem auch nix passiert ist."}]}||E3470F309F7A425184D6A2F45E72302F|||bplist00�
X$versionY$archiverT$topX$objects

So, the date is contained in this record (being 2021-7-21 in this case). So, simply doing this in the shell

sqlite3 DayOne.sqlite 'select zgregorianyear||"-"||zgregorianmonth||"-"||zgregorianday from zentry where zuuid="E3470F309F7A425184D6A2F45E72302F"'

will tell you this

2021-7-21

which seems is just what you need to look up your DT record. You could of course massage the SQL statement to return the name of this record, but given that it contains the weekday and the name of the month, that might be stretching it a bit.

Now, in JavaScript, I’d do this to get the date of this UUID:

let app = Application.currentApplication();
app.includeStandardAdditions = true;
let result = app.doShellScript(`sqlite3 "/Users/<yourname>/Library/Group Containers/5U8NS4GX82.dayoneapp2/Data/Documents/DayOne.sqlite" 'select zgregorianyear||"-"||zgregorianmonth||"-"||zgregorianday from zentry where zuuid="E3470F309F7A425184D6A2F45E72302F"'`);
console.log(result);

Replacing, of course, <yourname> with yourname without the pointy brackets. And this, finally, prints out “2001-7-21”, just as we want it to.

I would not want to figure out how that’s done in AppleScript with all the quoting and stuff, but I’m sure @BLUEFROG knows the answer to that.

So what you have to do is basically this:

  • find the link to your DO entry in the DT record (in JS, I’d do
  const match = record.plainText().match(/view\?entryId=(.*?\))/);
  const uuid = match[1];
  • feed that uuid into the shell command above
  • run that command and save its output in a variable
  • fool around with this variable to figure out the DT record’s name
  • get its uuid, build the DT link and add it to custom meta data

In fact, I’d rather add it right after the original DO link in the DT record. So that it’s clickable too, and you can have more than one link Something like

[DT data](x-devonthinkurl://<uuid>)

Disclaimer: I only tested the shell command, and only with one record. All the rest is … well, untested. Also, DayOne may decide to change their database schema at any point in time, and that may break the shell or the sql command. But until then, it should work.

1 Like

Yes, that’s correct, preserving the original Day One link in the DT markdown record (although see also later in this post, picking up on an idea from you). And, yes, there are records with more than one link but I could live with having one only as custom metadata for the relevant records.

I very much appreciate all you have done, which is most kind. I’m very impressed by the Day One manipulation. However, I am prepared to do that part manually and then work with AppleScript on the rest (if it’s possible!).

I’ve tried a slightly different approach to what I’ll call “the AppleScript part” (meaning findlng and linking in DT to a record the date of which is input manually):

set dAnswer to "01/07/2021"
set userEntry to display dialog ¬
	"Enter the link date as DD/MM/YYYY" default answer dAnswer
set userDate to text returned of userEntry
set linkDate to date userDate
set wDay to weekday of linkDate
set dateDay to day of linkDate
set dateMonth to month of linkDate
set dateYear to year of linkDate
set theLink to wDay & " " & dateDay & " " & dateMonth & " " & dateYear & ".md"


try
	tell application id "DNtp"
		set theDatabase to open database "/Users/stapp/Documents/DevonThink/Diaries.dtBase2"
		set foundRecord to search "\"" & theLink & "\"" in theDatabase
	end tell

That works well to return something like:

{content id 1173009 of database id 4 of application "DEVONthink 3"}

BUT I am at present struggling with how to obtain the UUID from foundRecord. However, I seem to have inched nearer to a potential solution — even if nowhere near so automated as yours. It might even be possible in due course to replace the Day One link in the DT record (which I would not mind doing if I could guarantee it would work!).

Please don’t think I’m ungrateful for your work with JavaScript. It’s just, for the time being at least, I’m happy to concentrate on some manual work and then AppleScript to automate the rest of the process.

Stephen

set UUID to UUID of foundRecord

Just like any other property

…which, in my latest script (as previously posted) produces the following error:

Error: Can’t get uuid of {content id 1173009 of database id 4 of application "DEVONthink 3"}.

Stephen

Ok, I didn’t look up the description of search in the dictionary. Where it clearly says that the return value is a list. I should have thought of that, because there might always be more than one record to match a search criterion.

So do something like

tell application id "DNtp"
	set theLink to "21.7.21"
	set foundRecords to search "\"" & theLink & "\""
	set theUuid to uuid of (first item of foundRecords)
end tell

…and the solution appears to be to remove

 of application "DEVONthink 3"

from the code quoted at the end of my earlier post. Work continues…

Stephen

No. One part of solution is to look closely at the output: {…} means that there’s a list. And the other part of the solution is to look closely at the documentation, where it says that search returns a list. I didn’t do neither of these parts, unfortunately.

We both posted at the same time. Your solution works perfectly and is (of course!) far more elegant than mine.

I have learned a lot from your patient tuition. Thanks so much for your time and help. It is really appreciated.

Stephen

I’ve posted my solution to your problem in a new thread

It is, of course, in JavaScript (and I’d rather do something very bad to myself than try to write that in AppleScript). I tried it out with (only) two records here, one linking to the other. And I decided to use “name==Wednesday 7 July 2021” as search criteria, assuming that you really named your files in DT that way. Otherwise, you’d have to change that (the search string, that is). When I used just “Wednesday 7 July 2021”, I got far too many results. Also, limiting the search to just the name should speed it up a bit.

You really like a challenge. dpn’t you? :grinning: That’s rather similar to me.

You drive me ever closer to need to try to understand JavaScript (which, for an AppleScript newbie, is moderately terrifying). I really appreciate what you have done and will try it with a subset of my data. I especially appreciate the careful annotations but no doubt shall manage to break something when working with it! (For example. the file names have .md extensions so I need to take account of that.)

I think my requirement is, as you say, very specialist so I do hope others can learn something useful from all you work.

Thanks again!

Stephen

Staying in this thread for the moment…I have:

  • loaded your JS in script editor;
  • changed your user name to mine;
  • added the path to my appropriate database

and run the script. I get the following error:

Error -1700: Can't convert types.

Because it’s JS I can’t even begin to debug so have to throw myself again at your mercy! Please don’t devote time to it if you’re busy. I continue developing something much less sophisticated in AppleScript.

Stephen

Please open the script editor’s message area (click on the three lines at the bottom of the window). You’ll get some context there. And then we’ll try to solve that.

Under Replies (which seems to provide the fullest context) I see:

app = Application("DEVONthink 3")
	app.database./Users/stapp/Documents/DevonThink/Diaries.dtBase2.root()
		--> Error -1700: Can't convert types.
Result:
Error -1700: Can't convert types.

which seems to hint that I’ve not properly added the name of the relevant DT database.

Stephen

The name has to be in qoutes. I suppose you left those out.
Here

const databaseName = "";

you just just enter Diaries between the quotes:

const databaseName = "Diaries";

No need for all the other stuff (Users/stapp etc): DT needs only the name of the database

Here’s the start of the code as now (and the database name was previously within the quotation marks):

(() => {
  const app = Application("DEVONthink 3");
  app.includeStandardAdditions = true; 
  const databaseName = "Diaries"; /* Add database name if you want to search for DT records only in this DB */

and here’s the error I still get:

app = Application("DEVONthink 3")
	app.database.Diaries.root()
		--> Error -1700: Can't convert types.
Result:
Error -1700: Can't convert types.

Stephen