EagleFiler applescript question

Hello Michael!

I am currently enrolled in a course called Building A Second Brain by Forte Labs, all about organizing and structuring your digital notes for optimal usability and retrievability. It’s awesome so far.

The author, Tiago Forte, is using Evernote as the example through the course but I have decided to implement my “second brain” using EagleFiler.

One concept Tiago has initiated is something called RandomNote - a script that will pull a random note from your library. This will inject some randomness into your mental input and can trigger new ideas. The originating applescript was created for Evernote and has later been built as an application that works on both macOS and Windows.

Naturally I would like to build a similar script for EagleFiler.

The aim of the script is to

  1. generate a list of all records that are children of a specific folder (exluding folder records)
  2. pick one of these records at random
  3. return the selected record. My thought is it could be either in QuickLook or in EF

The first step could also simply be a list of all non-folder records in the library.

But I can’t figure out how to do this. I am not new to applescript but not an expert either and would like some help. I have programming background so I don’t know if it’s just me not understanding applescript syntax or if it’s simply not possible.

I cannot even start with step 1. I tried with

tell application "EagleFiler"
    
    tell library document 1
                
        set _record to library record whose name is "1 Projects"
        
    end tell
end tell"

But get error "Can’t get library record whose name = “1 Projects” "

Is it possible to get all records that are children of a container? Is it possible to select a record based on values such as name, title, kind etc?

Here’s a script to do this. It’s not currently possible to select the chosen record in EagleFiler via AppleScript. I’ve written this script to mark the record by assigning the “random” tag to it. But you could certainly change it to open the file or reveal it in the Finder or something.

tell application "EagleFiler"
    tell library document 1
        set _record to library record "1 Projects" of root folder
        set _fileRecords to my nonFolders(my recursiveChildRecordsOf(_record))
        set _randomRecord to some item of _fileRecords
        my reveal(_randomRecord)
    end tell
end tell

on recursiveChildRecordsOf(_record)
    tell application "EagleFiler"
        set _result to {}
        repeat with _child in _record's library records
            copy _child to end of _result
            set _result to _result & my recursiveChildRecordsOf(_child)
        end repeat
        return _result
    end tell
end recursiveChildRecordsOf

on nonFolders(_records)
    tell application "EagleFiler"
        set _result to {}
        repeat with _record in _records
            if _record's universal type identifier is not "public.folder" then
                copy _record to end of _result
            end if
        end repeat
        return _result
    end tell
end nonFolders

on reveal(_record)
    tell application "EagleFiler"
        set _record's assigned tag names to _record's assigned tag names & {"random"}
    end tell
end reveal

Actually, I just momentarily forgot how to do this. Here’s an updated script that selects the random record:

tell application "EagleFiler"
    tell library document 1
        set _record to library record "1 Projects" of root folder
        set _fileRecords to my nonFolders(my recursiveChildRecordsOf(_record))
        set _randomRecord to some item of _fileRecords
        tell browser window 1 to set selected records to {_randomRecord}
    end tell
end tell

on recursiveChildRecordsOf(_record)
    tell application "EagleFiler"
        set _result to {}
        repeat with _child in _record's library records
            copy _child to end of _result
            set _result to _result & my recursiveChildRecordsOf(_child)
        end repeat
        return _result
    end tell
end recursiveChildRecordsOf

on nonFolders(_records)
    tell application "EagleFiler"
        set _result to {}
        repeat with _record in _records
            if _record's universal type identifier is not "public.folder" then
                copy _record to end of _result
            end if
        end repeat
        return _result
    end tell
end nonFolders

Wow, that is brilliant! I have decided to keep the “Archive” portion in a separate library, so then this script can search in all records of the current one. I modified this to check that the specific library is open. Finished script is:

tell application "EagleFiler"
    set _libraryPath to POSIX path of "**path to file**"
    set _libraryName to "**name of library.eflibrary**"
    
    -- make sure our document is open
    if not (exists (library document _libraryName)) then
        open _libraryPath
        repeat until exists library document _libraryName
            delay 0.5
        end repeat
    end if
    
    activate
    tell library document _libraryName
        set _fileRecords to my nonFolders(my recursiveChildRecordsOf(root folder))
        set _randomRecord to some item of _fileRecords
        set visible of browser window 1 to true
        tell browser window 1 to set selected records to {_randomRecord}
    end tell
end tell

on recursiveChildRecordsOf(_record)
    tell application "EagleFiler"
        set _result to {}
        repeat with _child in _record's library records
            copy _child to end of _result
            set _result to _result & my recursiveChildRecordsOf(_child)
        end repeat
        return _result
    end tell
end recursiveChildRecordsOf

on nonFolders(_records)
    tell application "EagleFiler"
        set _result to {}
        repeat with _record in _records
            if _record's universal type identifier is not "public.folder" then
                copy _record to end of _result
            end if
        end repeat
        return _result
    end tell
end nonFolders

Ok so this script works wonders on a small library.

I tried it with a library of ~3K records and the recursive lookup makes it unusably slow.

I’m wondering if it would be faster to pick any random record, check it’s identifier, and pick a new one until a non-folder is returned…

To test whether it’s looking up the identifier that’s slow, why don’t you try removing the call to nonFolders() and see if that helps?

If it’s building the record list that’s slow, you could just use every library record if you want to just look at everything instead of a particular subfolder.

Running the original script takes 62 seconds on my database with ~3K records.

Removing the nonFolders() call makes the script take 39 seconds so it’s an improvement, but not fast.

Now I changed the whole script to

tell application "EagleFiler"
	set _libraryPath to POSIX path of "** path to file **"
	set _libraryName to "** name of file**"
	
	-- make sure our document is open
	if not (exists (library document _libraryName)) then
		open _libraryPath
		repeat until exists library document _libraryName
			delay 0.5
		end repeat
	end if
	
	activate
	tell library document _libraryName
		set _fileRecords to every library record
		set _randomRecord to some item of _fileRecords
		repeat while _randomRecord's universal type identifier is "public.folder"
			set _randomRecord to some item of every library record
		end repeat
		set visible of browser window 1 to true
		tell browser window 1 to set selected records to {_randomRecord}
		set index of browser window 1 to 1
	end tell
end tell

And now the script finishes in 3 seconds!

The while loop seems to be correct, I can see in the script editors result window that it keeps taking a new record until a non-folder is found.

The only thing is that if another application than EF is active, the window will sometimes end up with no selection. Even though I can see in the Script Editor’s result window that a record has been chosen and selected.

I’ve been mucking around with order of making the window index 1 and making the selection but it doesn’t seem to make a difference. The reason I’m doing this is that I sometimes have several library windows open and my target window was hidden underneath and I wanted to bring it forward to show the selection.

You could probably make it faster by using:

set _randomRecord to some library record

inside of the loop so that it doesn’t have to fetch every record only to throw all but one away.

I’m not sure what’s happening there. I’ve been testing by running the scripts from another application and never seen that.