How can I keep a persistent key/value pair dictionary for this script?

Hi, I’m working on a script (using Script Debugger, of course) to create DEVONThink items from Zotero items. It’s sort of working. One bit I’d love help with is that I’m trying to maintain a persistent mapping for key/value pairs, the key being the Zotero citekey, and the value being the DEVONthink uuid. I’m using a plist to do this but that seems pretty slow and my code for doing it is a bit clunky. The script (work-in-progress, doesn’t work properly yet, etc.) is below. Any suggestions on how to do this better?

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

-- all the settings should be here
set bibJSONFile to "/Users/lyndon/repo/workflow/zot-export-bbt-short.json"
set thePListFile to "/Users/lyndon/repo/workflow/zotero-to-devonthink.plist"
set theTemplateFile to "/Users/lyndon/Library/Application Support/DEVONthink 3/Templates.noindex/Education/Reference LD.md"
set theDTDBFile to "/Users/lyndon/DevonThink/Research.dtBase2"
set theDTLocation to "/Library"

-- 
property NSJSONSerialization : a reference to current application's NSJSONSerialization
property NSData : a reference to current application's NSData
set theJSONData to NSData's dataWithContentsOfFile:(bibJSONFile)
set theJSON to NSJSONSerialization's JSONObjectWithData:theJSONData options:0 |error|:(missing value)

set bibjson to theJSON as record
set therefs to |items| of bibjson

tell application "System Events"
	if exists property list file thePListFile then
		set thePList to property list file thePListFile
	else
		set thePList to make new property list file with properties {name:thePListFile}
	end if
end tell

tell application id "DNtp"
	set theDatabase to open database theDTDBFile
	set theLocation to create location theDTLocation
	
	repeat with theRef in therefs
		set {theKey, theTitle, theURI, theZoteroID} to {citationKey, title, uri, itemID} of theRef
		
		try
			set theTitle to |shortTitle| of theRef
		end try
		
		set theGroupFile to theKey & " " & theTitle
		set theUUID to ""
		try
			tell application "System Events"
				tell property list file thePListFile
					set theUUID to value of property list item theKey
				end tell
			end tell
		end try
		
		if theUUID = "" then
			set theGroup to create location theDTLocation & "/" & theGroupFile
			set theUUID to uuid of theGroup
			tell application "System Events"
				tell property list file thePListFile
					try
						set value of property list item theKey to theUUID
					on error
						tell property list items
							make new property list item at end with properties {kind:string, name:theKey, value:theUUID}
						end tell
					end try
				end tell
			end tell
		else
			set theGroup to get record with uuid theUUID
			if theGroup is missing value then
				set theGroup to create location theDTLocation & "/" & theGroupFile
			end if
			set theUUID to uuid of theGroup
			tell application "System Events"
				tell property list file thePListFile
					try
						set value of property list item theKey to theUUID
					on error
						tell property list items
							make new property list item at end with properties {kind:string, name:theKey, value:theUUID}
						end tell
					end try
				end tell
			end tell
		end if
		
		set theDOI to ""
		try
			set theDOI to DOI of theRef
		end try
		
		set theAbstract to ""
		try
			set theAbstract to abstractNote of theRef
		end try
		
		set theDate to ""
		try
			set theDate to date of theRef
		end try
		
		set theURL to ""
		try
			set theURL to URL of theRef
		end try
		
		set theAttachmentPath to ""
		try
			repeat with theAttachment in |attachments| of theRef
				try
					if (title of theAttachment) ≠ "Snapshot" then
						try
							set theAttachmentPath to |path| of theAttachment
							set theAttachmentURI to uri of theAttachment
							tell theGroup
								set theAttachmentRecord to lookup records with path theAttachmentPath
								if theAttachmentReord is missing value or (count of theAttachmentRecord) is less than 1 then
									set theAttachmentRecord to indicate theAttachmentPath to theGroup
								else
									set theAttachmentRecord to item 1 of the AttachmentRecord
								end if
								set custom meta data of the AttachmentRecord to {|date|:theDate, DOI:theDOI, abstract:theAbstract, citekey:theKey, zoteroid:theZoteroID}
							end tell
						end try
					end if
				end try
			end repeat
		end try
		
		set theCreators to ""
		set multipleCreators to ""
		try
			repeat with theCreator in creators of theRef
				try
					set theCreators to theCreators & multipleCreators & |firstName| of theCreator & " " & |lastName| of theCreator & "(" & |creatorType| of theCreator & ")"
					set multipleCreators to " and "
				end try
			end repeat
		end try
	end repeat
end tell

You can use an NSMutableDictionary that you can read/write as a property list very simply.

I don’t have the apps you’re dealing with, but this should get you started:

set theUUID to missing value
set mainDict to current application's NSMutableDictionary's dictionaryWithContentsOfFile:thePListFile
if mainDict is not missing value then set theUUID to mainDict's objectForKey:theKey

if theUUID is missing value then
	tell application id "DNtp"
		set theGroup to create location theDTLocation & "/" & theGroupFile
		set theUUID to uuid of theGroup
	end tell
else
	set theUUI to the UUID as text
	tell application id "DNtp"
		set theGroup to get record with uuid theUUID
		if theGroup is missing value then set theGroup to create location theDTLocation & "/" & theGroupFile
		set theUUID to uuid of theGroup
	end tell
	mainDict's setObject:theUUID forKey:theKey
	mainDict's writeToFile:thPlistfile atomically:true
end if

One other thing: you should try to restrict code in application tell blocks to code relevant to the app, and not nest tell blocks if possible.

That’s super helpful. Thank you!

How can I initialise an empty NSMutableDictionary, e.g. if mainDict is missing value then

set mainDict to current application's NSMutableDictionary's new()
1 Like

@lyndondrake, there’s a lot of “stuff” going on in your script, so I might not have a clear picture, but it looks like you’re reading a JSON file and basically putting all the values from that into a property list file.

Since it’s possible (and very straightforward) to convert a JSON file to a property list file, would doing that meet the same objective as the one you’re trying to achieve here?

Can you demo how to convert a JSON file to a property list file? I’m actually wanting to take the data from the JSON file and add records to DEVONthink - the property list file is just to keep track of the mapping between IDs in the JSON file and the DEVONthink records.

It was Shane who posted an answer on that here

You can go more directly like this:

property NSPropertyListXMLFormat_v1_0 : a reference to 100
property NSPropertyListSerialization : a reference to current application's NSPropertyListSerialization
property NSJSONSerialization : a reference to current application's NSJSONSerialization
property NSData : a reference to current application's NSData

set theJSONData to NSData's dataWithContentsOfFile:(bibJSONFile)
set theJSON to NSJSONSerialization's JSONObjectWithData:theJSONData options:0 |error|:(missing value)
set theData to NSPropertyListSerialization's dataWithPropertyList:theJSON format:NSPropertyListXMLFormat_v1_0 options:0 |error|:(missing value)
theData's writeToFile:posixPath atomically:true