DT4 - show window to review changes

Files

This is the latest iteration of the script.

EDIT: Added a fix for selecting database

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use script "Dialog Toolkit Plus" version "1.1.3"
use script "RegexAndStuffLib" version "1.0.7"

property globalSuccessTags : {"auto-processed-invoice-content"}

-- Update UI fields
property fileNameField : {}
property companyField : {}
property locationPathField : {}
property databaseField : {}
property tagsField : {}
property presetsPopup : {}
property yearlyLocationCheckbox : {}
property originalFileNameLabel : {}

property currentYear : year of (current date)

property dialogPresets : {¬
	{presetTitle:"Global: No preset", yearlyLocation:true, basePath:"", databaseName:"", addTags:{}, companyName:""}, ¬
	{presetTitle:"Global: No rule", yearlyLocation:false, basePath:"/_Review/.NORULE", databaseName:"Inbox", addTags:{}, companyName:""}, ¬
	{presetTitle:"Global: Handbücher", yearlyLocation:false, basePath:"/Handbuecher", databaseName:"knowledge", addTags:{"handbuch"}, companyName:""}, ¬
	{presetTitle:"Global: Rente", yearlyLocation:true, basePath:"/Rente", databaseName:"business", addTags:{"rente"}, companyName:""}, ¬
	{presetTitle:"Global: Projekte", yearlyLocation:false, basePath:"/_Projekte/" & currentYear & "/CHANGEME", databaseName:"personal", addTags:{"projekt"}, companyName:""}, ¬
	{presetTitle:"Global: Sonstiges-Behoerden", yearlyLocation:true, basePath:"/Vertraege-und-Rechnungen/_Sonstiges-Behoerden", databaseName:"business", addTags:{"verwaltung", "behoerden"}, companyName:""}, ¬
	{presetTitle:"Global: Sostiges-Gesundheit", yearlyLocation:true, basePath:"/Vertraege-und-Rechnungen/_Sonstiges-Gesundheit", databaseName:"business", addTags:{"health", "gesundheit"}, companyName:""}, ¬
	{presetTitle:"Global: Sonstiges-Kaufbelege", yearlyLocation:true, basePath:"/Vertraege-und-Rechnungen/_Sonstiges-Kaufbelege", databaseName:"business", addTags:{"rechnungen", "invoice"}, companyName:""}, ¬
	{presetTitle:"Global: Tipps", yearlyLocation:false, basePath:"/Tipps/Assets", databaseName:"knowledge", addTags:{"tipp"}, companyName:""}, ¬
	{presetTitle:"Global: Urlaub", yearlyLocation:true, basePath:"/_Urlaub", databaseName:"personal", addTags:{"urlaub", "holidays"}, companyName:""}, ¬
	{presetTitle:"Global: Vorlagen", yearlyLocation:false, basePath:"/_Neu", databaseName:"templates", addTags:{"template"}, companyName:""} ¬
		}
-----------
-- code to run within Script Debugger
-- tell application "MstyStudio" to activate
-- tell application id "DNtp" to activate

tell application id "DNtp" to my performSmartRule(selected records)
-- test scriptOutput
-- tell application id "DNtp" to my scriptOutput(selected record 1, "")
-----------

-- test performSmartRule
property globalIsDev : true

-- copy template for smart rules
on performSmartRule(theRecords)
	local libraryHandler
	set libraryHandler to script "library-handling"
	
	tell application id "DNtp"
		set currentDb to current database
	end tell
	
	libraryHandler's canRun(currentDb, theRecords)
	
	local scriptName
	set scriptName to "document-invoices-handle"
	
	local theScriptHandler
	set theScriptHandler to libraryHandler's loadScript(scriptName, globalIsDev)
	
	theScriptHandler's globalLog("INFO", "Run script", "script=" & quoted form of scriptName)
	
	theScriptHandler's ForAll(theRecords, globalIsDev)
	
	theScriptHandler's globalLog("INFO", "Script finished", "script=" & quoted form of scriptName)
end performSmartRule

-- Run job for all given records
on ForAll(theRecords, localIsDev)
	local titleProgressBar
	set titleProgressBar to "Handle documents"
	
	tell application id "DNtp"
		show progress indicator titleProgressBar steps (count of theRecords) with cancel button
	end tell
	-- https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/reference/ASLR_error_xmpls.html
	
	set len to length of theRecords
	set i to 1
	
	my globalLog("INFO", quoted form of "Job started", "count_of_records=" & len)
	
	try
		local theRecord
		repeat with theRecord in theRecords
			tell application id "DNtp"
				if cancelled progress then
					my recordLog(theRecord, "INFO", quoted form of "Cancel current run of script", missing value)
					
					exit repeat
				end if
			end tell
			
			tell application id "DNtp"
				set theFilename to the filename of theRecord
				step progress indicator theFilename
			end tell
			
			my recordLog(theRecord, "INFO", quoted form of "Work on record", "index=" & i & "/" & len)
			
			local modifiedRecord
			set modifiedRecord to my ForOne(theRecord, localIsDev)
			
			if modifiedRecord is not missing value then
				tell application id "DNtp"
					set tags of modifiedRecord to (get tags of modifiedRecord) & globalSuccessTags
				end tell
			end if
			
			set i to i + 1
		end repeat
	on error errMsg number errNum partial result partialError
		
		tell application id "DNtp"
			hide progress indicator
		end tell
		
		-- if errNum = -10000 then
		--  	my globalLog("WARN", quoted form of "Job execution aborted", "error=-10000 count_of_records=" & len)
		--	return
		-- end if
		
		if errNum = 1000 then
			my globalLog("WARN", quoted form of "Job execution aborted by user", "error=1000 count_of_records=" & len)
			return
		end if
		
		set AppleScript's text item delimiters to {return}
		-- An unknown error occurred. Resignal, so the caller
		-- can handle it, or AppleScript can display the number
		
		display alert errMsg & ("Error number: ") & errNum & return & (partialError as text)
		error errMsg & ("Error number: ") & errNum & return & (partialError as text)
	end try
	
	tell application id "DNtp"
		hide progress indicator
	end tell
	
	my globalLog("INFO", quoted form of "Job completed", "count_of_records=" & len)
end ForAll

-- Run job for single record
on ForOne(theRecord, localIsDev)
	local thisYear
	tell application id "DNtp"
		set thisYear to year of (get creation date of theRecord) as string
	end tell
	
	-- repeat for "redo" of a single record
	-- * load rules
	-- * set metadata from rules
	-- * show dialog
	
	local dialogResponse
	set dialogResponse to missing value
	
	-- create window to show record and check it with given metadata
	local theWindow
	tell application id "DNtp"
		set theWindow to open window for record theRecord
	end tell
	
	repeat
		set dialogResponse to my showDialogInLoop(theRecord, dialogResponse, thisYear, localIsDev)
		
		if exitLoop of dialogResponse is true then
			exit repeat
		end if
	end repeat
	
	local theButtonPressed
	set theButtonPressed to buttonPressed of dialogResponse
	
	-- handle buttons
	if theButtonPressed = "Ignore" or theButtonPressed = "Gave up" then
		my recordLog(theRecord, "DEBUG", quoted form of "Button pressed by user", "button=" & theButtonPressed & return & "action=" & quoted form of "Ignore file due to user interaction or time out")
		
		-- if windows has been already closed prevents error
		try
			tell application id "DNtp"
				close theWindow
			end tell
		end try
		
		return missing value
	end if
	
	if theButtonPressed = "Abort" then
		my recordLog(theRecord, "DEBUG", quoted form of "Button pressed by user", "button=" & theButtonPressed & return & "action=" & quoted form of "Stop script interaction")
		
		-- no: close theWindow to make review and handling easier
		error "Abort handling of documents" number 1000
	end if
	
	if theButtonPressed = "Delete" then
		my recordLog(theRecord, "DEBUG", quoted form of "Button pressed by user", "button=" & theButtonPressed & return & "action=" & quoted form of "Move file to trash")
		
		-- if windows has been already closed prevents error
		try
			tell application id "DNtp"
				close theWindow
			end tell
		end try
		
		tell application id "DNtp"
			set deleteAlertResult to display alert "Delete record?" buttons {"Yes", "No"} default button "No" giving up after 0
		end tell
		
		if button returned of deleteAlertResult = "Yes" then
			tell application id "DNtp"
				move record theRecord to trash group of (database of theRecord)
			end tell
		end if
		
		return missing value
	end if
	
	my recordLog(theRecord, "DEBUG", quoted form of "Button pressed by user", "button=" & theButtonPressed & return & "action=" & quoted form of "Set attributes and location of record based on rules")
	
	-- get information about record from dialog
	local parsedDialogResponse
	set parsedDialogResponse to my parseDialogResponse(dialogResponse)
	
	-- get metadata from response
	local updatedTags, existingTags, theDatabaseName, theCompany, locationPath, theButtonPressed
	set updatedTags to (updatedTags of parsedDialogResponse)
	set existingTags to (existingTags of parsedDialogResponse)
	set theDatabaseName to (databaseName of parsedDialogResponse)
	set theCompany to (companyName of parsedDialogResponse)
	set locationPath to (locationPath of parsedDialogResponse)
	set newFilename to (newFilename of parsedDialogResponse)
	
	-- if windows has been already closed prevents error
	try
		tell application id "DNtp"
			close theWindow
		end tell
	end try
	
	-- set name of record	
	local newName
	local oldName
	
	tell application id "DNtp"
		set oldName to name of theRecord
		set newName to newFilename of dialogResponse
		
		set name of theRecord to newName
	end tell
	
	-- create location
	local oldLocation
	tell application id "DNtp"
		set oldLocation to location of theRecord
	end tell
	
	local newLocation
	tell application id "DNtp"
		set newLocation to create location locationPath in database theDatabaseName
	end tell
	
	-- get database from location
	local theDatabase
	tell application id "DNtp"
		set theDatabase to (database of newLocation)
	end tell
	
	-- update tags
	if updatedTags is missing value then
		set updatedTags to {}
	end if
	
	if existingTags is missing value then
		set existingTags to {}
	end if
	
	tell application id "DNtp"
		set tags of theRecord to updatedTags & existingTags
	end tell
	
	-- set company name (custom attribute) from metadata
	if (companyName of parsedDialogResponse) is not missing value then
		tell application id "DNtp"
			add custom meta data companyName of parsedDialogResponse for "company" to theRecord
		end tell
	end if
	
	-- move record to new location
	local movedRecord
	tell application id "DNtp"
		set movedRecord to move record theRecord to newLocation
	end tell
	
	tell application id "DNtp"
		set newLocationName to location with name of newLocation
	end tell
	
	my recordLog(movedRecord, "INFO", quoted form of "Moved and renamed document", "old_location=" & quoted form of oldLocation & return & "new_location=" & quoted form of (newLocationName) & return & "old_name=" & oldName & return & "new_name=" & newName, missing value)
	
	return movedRecord
end ForOne

on showDialogInLoop(theRecord, dialogResponse, thisYear, localIsDev)
	local libraryHandler
	set libraryHandler to script "library-handling"
	
	-- (re)load rules
	local theScriptHandler
	set theScriptHandler to libraryHandler's loadScriptSilent("document-invoices-rules", localIsDev)
	
	tell application id "DNtp"
		local theFilename
		set theFilename to filename of theRecord
	end tell
	
	-- use rules to get metadata about file
	-- default is missing value if no rule matches
	local recordMetadata
	set recordMetadata to theScriptHandler's getMetadataForRecord(theRecord, thisYear)
	-- set recordMetadata to my getMetadataForRecord(theRecord, thisYear)
	
	if recordMetadata is missing value then
		my recordLog(theRecord, "WARN", quoted form of "No matching rule. Ignore file and go on", missing value)
		error "File: " & theFilename & " has no matching rule. I ignore this file and move on if multiple files are selected"
	end if
	
	if (basePath of recordMetadata) is missing value or (databaseName of recordMetadata) is missing value then
		display alert "DNtp" message "basePath or database is missing for the file " & theFilename & ". This is not allowed. Going on with the next file."
		error "basePath or database is missing for the file " & theFilename & ". This is not allowed. Going on with the next file."
	end if
	
	-- show dialog and ask user for "help"
	set dialogResponse to my showDialog(theRecord, recordMetadata, dialogResponse, thisYear)
	
	local theButtonPressed
	set theButtonPressed to buttonPressed of dialogResponse
	
	if (theButtonPressed is not "Redo" and theButtonPressed is not "AI Rename") then
		set exitLoop of dialogResponse to true
		return dialogResponse
	end if
	
	-- nothing to to with theButtonPressed = "Redo"
	
	-- ask model for new name file if AI rename script is asked
	if theButtonPressed = "AI Rename" then
		my recordLog(theRecord, "DEBUG", quoted form of "Button pressed by user", "button=" & theButtonPressed & return & "action=" & quoted form of "Asking AI for new filename")
		
		set exitLoop of dialogResponse to false
		
		local libraryHandler
		set libraryHandler to script "library-handling"
		
		local theScriptHandler
		set theScriptHandler to libraryHandler's loadScriptSilent("document-rename", localIsDev)
		
		local thePrompt
		set thePrompt to globalPrompt of theScriptHandler
		
		local aiRequestTimeout
		set aiRequestTimeout to globalAiRequestTimeout of theScriptHandler
		
		local aiEngine
		tell application id "DNtp"
			set aiEngine to Ollama
		end tell
		
		local newAiGeneratedName
		set aiGeneratedName to theScriptHandler's GetNewNameLoop(theRecord, localIsDev, aiEngine, "phi4", thePrompt, aiRequestTimeout)
		
		set newFilename of dialogResponse to aiGeneratedName
	end if
	
	my recordLog(theRecord, "DEBUG", quoted form of "Button pressed by user", "button=" & theButtonPressed & return & "action=" & quoted form of "Re-evaluate rules")
	
	return dialogResponse
end showDialogInLoop

on parseDialogResponse(dialogResponse)
	local companyName, updatedTags, existingTags, databaseName, locationPath, addedYear, taxYear, locationPath, yearlyLocation
	set addedYear to (addedYear of dialogResponse)
	set taxYear to (taxYear of dialogResponse)
	set newFilename to (newFilename of dialogResponse)
	set companyName to (companyName of dialogResponse)
	set databaseName to (databaseName of dialogResponse)
	set updatedTags to (updatedTags of dialogResponse)
	set existingTags to (existingTags of dialogResponse)
	set locationPath to (locationPath of dialogResponse)
	set yearlyLocation to (yearlyLocation of dialogResponse)
	
	-- if user set year where this document is relevant for tax
	if taxYear is not "" then
		set taxYear to taxYear as number
		set updatedTags to updatedTags & {"taxes-" & taxYear}
	end if
	
	-- make sure we append the year the record was added to the database
	if yearlyLocation is true then
		set locationPath to (locationPath & "/" & addedYear)
	end if
	
	-- "missing value" is correct!
	if newFilename is "missing value" or newFilename is "" or newFilename is missing value then
		error "Missing filename"
	end if
	
	-- "missing value" is correct!
	if locationPath is "missing value" or locationPath is "" or locationPath is missing value then
		error "Undefined individual location for file: Either select a preset with default locations or an individual one"
	end if
	
	-- return gathered metadata
	return {newFilename:newFilename, companyName:companyName, updatedTags:updatedTags, existingTags:existingTags, databaseName:databaseName, locationPath:locationPath}
end parseDialogResponse

on showDialog(theRecord, recordMetadata, previousDialogResponse, thisYear)
	-- gather data
	local originalFilename
	local newFilename
	local existingTags
	
	tell application id "DNtp"
		set originalFilename to (filename of theRecord)
		set existingTags to (tags of theRecord)
	end tell
	
	if previousDialogResponse is missing value then
		tell application id "DNtp"
			set newFilename to (name without extension of theRecord)
		end tell
	else if newFilename of previousDialogResponse is not missing value then
		set newFilename to (newFilename of previousDialogResponse)
	else if newFilename of recordMetadata is not missing value then
		set newFilename to (newFilename of recordMetadata)
	else
		tell application id "DNtp"
			set newFilename to (name without extension of theRecord)
		end tell
	end if
	
	local yearlyLocation
	set yearlyLocation to (yearlyLocation of recordMetadata)
	
	local theDatabaseName
	set theDatabaseName to (databaseName of recordMetadata)
	
	local theCompanyName
	set theCompanyName to (companyName of recordMetadata)
	
	local updatedTags
	set updatedTags to (addTags of recordMetadata)
	
	local theLocationPath
	set theLocationPath to (basePath of recordMetadata)
	
	local taxYear
	set taxYear to (taxYear of recordMetadata)
	
	-- missing value cannot be handled by dialogue
	-- optional
	
	if yearlyLocation is missing value then
		set yearlyLocation to false
	end if
	
	if theCompanyName is missing value then
		set theCompanyName to ""
	end if
	
	if updatedTags is missing value then
		set updatedTags to {}
	end if
	
	if taxYear is missing value then
		set taxYear to ""
	end if
	
	set accViewWidth to 600
	
	-- set buttons
	set {theButtons, minWidth} to create buttons {"Delete", "AI Rename", "Redo", "Abort", "Ignore", "Move"} default button "Ignore" without equal widths
	if minWidth > accViewWidth then set accViewWidth to minWidth -- make sure buttons fit
	
	-- to make it look better, we can get the length of the longest label we will use, and use that to align the controls
	set theLabelStrings to {"Location year", "Tax year", "Company name", "Location", "Database", "New tags", "Existing tags", "Chosen preset", "New filename"}
	set maxLabelWidth to max width for labels theLabelStrings
	set controlLeft to maxLabelWidth + 8
	
	-- set names for the presets used in dropdown
	-- needs to be in the
	local presetNames
	set presetNames to {}
	repeat with preset in dialogPresets
		set end of presetNames to (presetTitle of preset)
	end repeat
	
	-- empty added year to workaround namespace issue
	local addedYear
	set addedYear to ""
	
	if yearlyLocation is true then
		set addedYear to thisYear
	end if
	
	-- space between items
	local spacer
	set spacer to 20
	
	local joinedUpdatedTags
	-- place tags into single input field by joining them into single string
	set joinedUpdatedTags to (join strings updatedTags using delimiter ",")
	
	local joinedExistingTags
	set joinedExistingTags to (join strings existingTags using delimiter ",")
	
	-- make current record the no preset record
	set oldPreset to item 1 of dialogPresets
	set newPreset to {presetTitle:"Global: No preset", yearlyLocation:yearlyLocation, basePath:theLocationPath, databaseName:theDatabaseName, addTags:updatedTags, companyName:theCompanyName}
	set dialogPresets to {newPreset} & (items 2 thru -1 of dialogPresets)
	
	-- get list of known databases from DT
	set sortedListOfDatabases to my getDatabaseList()
	
	-- start from the bottom
	set {taxesYearField, taxesYearLabel, theTop, fieldLeft} to create side labeled field taxYear placeholder text "Tax relevant" left inset (accViewWidth / 2) + spacer bottom 8 total width (accViewWidth / 2) - spacer label text (item 2 of theLabelStrings) field left controlLeft
	
	local addedDocYearField
	set {addedDocYearField, addedDocYearLabel, theTop, fieldLeft} to create side labeled field addedYear placeholder text "Added date" left inset 0 bottom 8 total width (accViewWidth / 2) - spacer label text (item 1 of theLabelStrings) field left controlLeft
	set {yearlyLocationCheckbox, theTop, newWidth} to create checkbox "Create yearly location" left inset controlLeft bottom (theTop + 16) max width accViewWidth initial state yearlyLocation
	set {locationPathField, locationPathLabel, theTop, fieldLeft} to create side labeled field theLocationPath placeholder text "The path to location in DB" left inset 0 bottom (theTop + 16) total width accViewWidth label text (item 4 of theLabelStrings) field left controlLeft
	set {databaseField, databaseLabel, theTop, fieldLeft} to create labeled popup sortedListOfDatabases left inset 0 bottom (theTop + 14) popup width 435 max width accViewWidth label text (item 5 of theLabelStrings) popup left controlLeft initial choice theDatabaseName
	set {companyField, companyLabel, theTop, fieldLeft} to create side labeled field theCompanyName placeholder text "the name of the company" left inset 0 bottom (theTop + 16) total width accViewWidth label text (item 3 of theLabelStrings) field left controlLeft
	
	set {existingTagsField, existingTagsLabel, theTop} to create top labeled field joinedExistingTags placeholder text "List of tags" bottom (theTop + 3) field width accViewWidth - controlLeft label text (item 7 of theLabelStrings) left inset controlLeft extra height 30
	set {tagsField, tagsLabel, theTop} to create top labeled field joinedUpdatedTags placeholder text "List of tags" bottom (theTop + 3) field width accViewWidth - controlLeft label text (item 6 of theLabelStrings) left inset controlLeft extra height 30
	
	set {originalFileNameField, theTop} to create label "Original filename with ext: " & originalFilename bottom theTop + 16 max width (accViewWidth - 100) left inset controlLeft
	set {fileNameField, fileNameLabel, theTop} to create top labeled field newFilename placeholder text "The new filename" bottom (theTop + 3) field width accViewWidth - controlLeft label text (item 9 of theLabelStrings) left inset controlLeft extra height 60
	
	set {presetsPopup, presetsLabel, theTop} to create labeled popup presetNames left inset 0 bottom (theTop + 14) popup width 435 max width accViewWidth label text (item 8 of theLabelStrings) popup left controlLeft initial choice "Global: No preset"
	
	-- make it possible that the dropdown updates other fields
	-- https://www.macscripter.net/t/change-elements-dynamically-using-shanes-dialog-toolkit/70409/11
	presetsPopup's setTarget:me
	presetsPopup's setAction:"updateOtherFields:"
	
	-- make list of cotronls and pass to display command
	set allControls to {yearlyLocationCheckbox, addedDocYearField, addedDocYearLabel, taxesYearField, taxesYearLabel, companyField, companyLabel, locationPathField, locationPathLabel, databaseField, databaseLabel, existingTagsField, existingTagsLabel, tagsField, tagsLabel, originalFileNameField, fileNameField, fileNameLabel, presetsPopup, presetsLabel}
	
	-- controlResults will in the same order as allControls
	local buttonPressed, controlsResults
	set {buttonPressed, controlsResults} to display enhanced window "Set metadata for file" acc view width accViewWidth acc view height theTop acc view controls allControls buttons theButtons initial position {} giving up after 0 with align
	
	-- debugging
	-- my updateOtherFields()
	
	-- gather data from result
	local newFilename, companyName, updatedTags, existingTags, databaseName, locationPath, taxYear, locationPath, chosenPreset, yearlyLocation
	set yearlyLocation to (item 1 of controlsResults)
	set addedYear to (item 2 of controlsResults)
	set taxYear to (item 4 of controlsResults)
	set companyName to (item 6 of controlsResults)
	set locationPath to (item 8 of controlsResults)
	set databaseName to (item 10 of controlsResults)
	set existingTags to split string (item 12 of controlsResults) using delimiters ","
	set updatedTags to split string (item 14 of controlsResults) using delimiters ","
	set newFilename to (item 17 of controlsResults)
	set chosenPreset to (item 19 of controlsResults)
	
	my recordLog(theRecord, "DEBUG", quoted form of "Result of dialog with user", "new_filename=" & newFilename & return & "yearly_location=" & yearlyLocation & return & "chosen_prese=" & chosenPreset & return & "button_pressed=" & buttonPressed & return & "added_year=" & addedYear & return & "tax_year=" & taxYear & return & "company_name=" & companyName & return & "database_name=" & databaseName & return & "existing_tags=" & (join strings existingTags using delimiter ",") & return & "updated_tags=" & (join strings updatedTags using delimiter ",") & return & "location_path=" & locationPath)
	
	return {exitLoop:missing value, newFilename:newFilename, yearlyLocation:yearlyLocation, presetNames:presetNames, chosenPreset:chosenPreset, buttonPressed:buttonPressed, addedYear:addedYear, taxYear:taxYear, companyName:companyName, databaseName:databaseName, updatedTags:updatedTags, existingTags:existingTags, locationPath:locationPath}
end showDialog

-- update fields by using information from the dropdown
-- requires framework "Foundation" and properties for fields to be updated 
-- on updateOtherFields()
on updateOtherFields:sender
	try
		-- set index of selected dropdown item
		local selectedPresetIndex
		set selectedPresetIndex to (my presetsPopup's indexOfSelectedItem() as integer) + 1
		set selectedPreset to item selectedPresetIndex of dialogPresets
		
		-- get values for selected dropdown
		local selectedCompanyName, selectedLocation, selectedTags, joinedTags, selectedDatabase, selectedYearlyLocation
		set selectedCompanyName to (companyName of selectedPreset as text)
		set selectedLocation to (basePath of selectedPreset as text)
		
		local selectedTags
		set selectedTags to (addTags of selectedPreset)
		
		set joinedTags to (join strings selectedTags using delimiter ",")
		
		set selectedDatabase to (databaseName of selectedPreset as text)
		set selectedYearlyLocation to (yearlyLocation of selectedPreset as boolean)
		
		-- set values
		-- https://www.macscripter.net/t/change-elements-dynamically-using-shanes-dialog-toolkit/70409/11
		my (companyField's setStringValue:selectedCompanyName)
		my (locationPathField's setStringValue:selectedLocation)
		my (tagsField's setStringValue:joinedTags)
		my (yearlyLocationCheckbox's setState:selectedYearlyLocation)
		
		local dbList
		set dbList to getDatabaseList()
		
		local dbIndex
		repeat with n from 1 to count of dbList
			if selectedDatabase is (item n of dbList) then
				set dbIndex to n - 1
				exit repeat
			end if
		end repeat
		
		my (databaseField's selectItemAtIndex:dbIndex)
	on error errMsg number errNum partial result partialError
		set AppleScript's text item delimiters to {return}
		-- An unknown error occurred. Resignal, so the caller
		-- can handle it, or AppleScript can display the number.
		display alert errMsg & ("Error number: ") & errNum & return & (partialError as text)
		error errMsg & ("Error number: ") & errNum & return & (partialError as text)
	end try
end updateOtherFields:

on recordLog(theRecord, level, msg, msgInfo)
	if msgInfo is missing value then
		set logMsg to "msg=" & msg
	else
		set logMsg to "msg=" & msg & " " & msgInfo
	end if
	
	if level is not "DEBUG" then
		tell application id "DNtp"
			log message record theRecord info "level=INFO " & logMsg
		end tell
		
		return
	end if
	
	if globalIsDev is true then
		tell application id "DNtp"
			log message record theRecord info "level=DEBUG " & logMsg
		end tell
	end if
end recordLog

on globalLog(level, msg, msgInfo)
	if msgInfo is missing value then
		set logMsg to "msg=" & msg
	else
		set logMsg to "msg=" & msg & " " & msgInfo
	end if
	
	if level is not "DEBUG" then
		tell application id "DNtp"
			log message "level=INFO " & logMsg
		end tell
		
		return
	end if
	
	if globalIsDev is true then
		tell application id "DNtp"
			log message "level=DEBUG " & logMsg
		end tell
	end if
end globalLog

on getDatabaseList()
	local listOfDatabases
	tell application id "DNtp"
		set listOfDatabases to name of every database
	end tell
	
	-- sort list of databases
	-- https://stackoverflow.com/questions/78218289/applescript-how-do-i-sort-a-list-of-sentences-paragraphs-with-an-assigned-numbe
	set array to current application's NSArray's arrayWithArray:listOfDatabases
	set sortedListOfDatabases to (array's sortedArrayUsingSelector:"localizedStandardCompare:") as list
	
	return sortedListOfDatabases
end getDatabaseList