Yank recently modified files to current location

Here’s an experimental script I’ve been working on to yank the most recently modified files to the currently viewed group. The script can be easily modified to allow for replication instead of moving, but I prefer moving.

I’d like to see the script support to work on the most recently added files instead of the most recently modified files, but Devonthink’s scripting search command does not have the ability to search on “addition date.” I’m hoping that that may someday be possible. I know christian has plenty of work for version 2 final. But maybe thereafter? 8)

But for now, this seems to work well for me if you assign it a keystroke. It ignores docs in the trash or those already in the current group, so you can repeatedly invoke it to yank more and more files to the present.

I’m sure this one has bugs in it. Please report them and I’ll update the script. Hopefully it is useful to someone !

best,

Erico




---Recent File Yank to current Group
---Grab most recently modified record from all open devonthink databases and insert in current viewer window. 
---Helpful for when Devonthink inserts a captured pdf or text  where you didn't expect it to!
---This version will not move records from the trash, and it will not move records having a parent in the current group.  The latter is a feature nice because you can keep issuing the command to pull all the last items to the current group: it should  successively move backwards through each "older" item, until it hits the hour threshhold in specified below.  
--- the script could be modified to replicate instead of move, if so desired.


---Future Feature Request: It would be nice if Devonthink allowed one to *search* on addition date rather than just *modification date*.  Ideally, one would have a script that could detected recently added (as well as merely recently modified) documents.   Currently, if a file is dragged into the inbox and has an older modification date (in the filesystem), devonthink's applescripting command is not  able to find this document, even if it was just added to the database.  It would be a nice feature addition to support searches on "addition date"


--this script requires Mac Growl to be installed for notifications.


---By Eric Oberle, version .4a


property retrospection_threshhold : hours * 4


tell application "DEVONthink Pro"
	
	----List of types to move  (ignore all others)
	set kinds_to_move to {"Image", "PDF", "PDF+Text", "Web Archive", "TEXT", "Bookmark", "RTF", "RTFD", "eml"}
	set kinds_to_exclude to {"Group", "Smart Group", "Feed", "HTML"}
	
	set text_was_selected to false
	set all_databases to every database
	
	
	----Try to figure out where to put the record
	set the_Window to viewer window 1
	set win_name to get name of the_Window
	set viewer_window_record to content record of the_Window
	set viewer_current_group to current group
	set skip_record_if_it_has_one_of_these_parents to {viewer_current_group}
	
	---if no record is selected, but we have a group use that.
	if viewer_window_record is missing value then
		set viewer_window_record to current group
	end if
	
	---see if we can identify parent of current record
	try
		set viewer_window_record_parent to parent 1 of viewer_window_record
	on error
		set viewer_window_record_parent to viewer_window_record
	end try
	set viewer_window_record_location to location of viewer_window_record
	---log " viewer_window_record_parent " & ((name of viewer_window_record_parent) as string)
	
	
	
	---do the search
	set newest_record to my return_newest_record(retrospection_threshhold, all_databases, kinds_to_exclude, skip_record_if_it_has_one_of_these_parents)
	
	---the following line uses explicit include
	---set newest_record to my return_newest_record(retrospection_threshhold, all_databases, kinds_to_move, skip_record_if_it_has_one_of_these_parents)
	
	
	
	if newest_record is {} then
		display dialog "Sorry,but Devonthink found  no records less than " & ((retrospection_threshhold / hours) as string) & " hours old. "
		error number -128
		return
	end if
	
	log "moving to " & name of viewer_current_group
	set the_parent to parent 1 of newest_record
	set record_name to name of newest_record
	log "Moving  " & record_name & "from... " & ((name of the_parent) as string)
	my growlNotification("DEVONthink Pro", "Moving " & record_name, "from " & ((location of the_parent) & name of the_parent))
	
	move record newest_record to viewer_current_group
end tell






on return_newest_record(max_age, the_databases, types_searched, groups_to_ignore)
	set old_date to (current date) - (100 * days)
	
	set newest_record to {}
	using terms from application "DEVONthink Pro"
		tell application "DEVONthink Pro"
			repeat with the_database in the_databases
				
				log "***** now searching ***" & (get name of the_database) as string
				set db_root to root of the_database
				set id_db_trash to id of trash group of the_database
				---it would be *really nice* if devonthink's search command allowed one to search on addition date
				set new_recs to search "" age max_age in db_root
				
				
				repeat with the_record in new_recs
					set exception_found to false
					log "reviewing record " & (name of the_record as string)
					set the_parents_id to id of every parent of the_record
					set ids_to_ignore to {}
					repeat with x in groups_to_ignore
						set y to get id of x
						set ids_to_ignore to ids_to_ignore & {y}
					end repeat
					set ids_to_ignore to ids_to_ignore & {id_db_trash}
					
					---log "exception list "
					---log (ids_to_ignore)
					repeat with exception_id in ids_to_ignore ---groups_to_ignore
						---set exception_id to ((get id of this_exception) as number)
						repeat with this_parent_id in the_parents_id
							---set this_parent to the first item of this_parent
							log ("exception" & exception_id as string) & "vs. parent " & this_parent_id as string
							
							if (this_parent_id as number) is equal to (exception_id as number) then
								
								set exception_found to true
								set parent_rec to get record with id exception_id in the_database
								log "parent excluded: " & (name of parent_rec as string)
								exit repeat
								
							end if
						end repeat
						if exception_found then exit repeat
					end repeat
					
					---if ((get kind of the_record)) is in types_searched and exception_found is false then
					if ((get kind of the_record) is not in types_searched) and (exception_found is false) then
						log "kind match"
						set the_name to get name of the_record
						set new_date to get modification date of the_record
						if (new_date > old_date) then
							log "Found a newer one…" & name of the_record
							set newest_record to the_record
							set old_date to new_date
						end if
					end if
				end repeat
			end repeat
			return newest_record
		end tell
	end using terms from
end return_newest_record




on growlNotification(growlIcon, growlTitle, growlDescrip)
	
	if application "GrowlHelperApp" is running then
		set appName to "ericsdtnotify"
		set notifs to {growlTitle}
		
		tell application "GrowlHelperApp"
			register as application ¬
				appName all notifications notifs ¬
				default notifications notifs ¬
				icon of application growlIcon
			notify with name growlTitle title growlTitle description growlDescrip application name appName
		end tell
	end if
	return ""
end growlNotification


The “age” parameter is basically an obsolete relict of v1.x and still supported for compatibility. Here’s an AppleScript example returning all contents added in the last 24 hours:


tell application "DEVONthink Pro"
	set theDate to current date
	set theDate to theDate - (3600 * 24)
	set theDatabase to current database
	-- Doesn't include groups, smart groups or feeds
	set theAddedContents to contents of theDatabase whose addition date ≥ theDate
end tell

Not that slow if executed via DEVONthink Pro’s Scripts menu.

Christian,

Thank you for that suggestion! I keep forgetting that one could pass a “whose” qualifier to the content record in that way. It doesn’t seem as fast as the search command (probably because Applescript is doing the work, rather than DT), but it is bearable.

So now I have two scripts. The top one does date modified, the one below does date added. I hope people find them useful. I find it great when I have a lot of databases open, and I am having trouble multitasking.

I have to say, it’s amazing how flexible the DT applescript implementation is. Thanks!

-erico



---Recent File Yank to current Group
---Grab most recently added record from all open devonthink databases and insert in current viewer window. 
---Helpful for when Devonthink adds a captured pdf or text  where you didn't expect it to!
---This version will not move records from the trash, and it will not move records having a parent in the current group.  The latter is a feature nice because you can keep issuing the command to pull all the last items to the current group: it should  successively move backwards through each "older" item, until it hits the hour threshhold in specified below.  
--- the script could be modified to replicate instead of move, if so desired.
--this script requires Mac Growl to be installed for notifications.

---By Eric Oberle, version .5a

property retrospection_threshhold : hours * 5


tell application "DEVONthink Pro"
	
	----List of types to move  (ignore all others)
	set kinds_to_move to {"Image", "PDF", "PDF+Text", "Web Archive", "TEXT", "Bookmark", "RTF", "RTFD", "eml"}
	set kinds_to_exclude to {"Group", "Smart Group", "Feed", "HTML"}
	
	set text_was_selected to false
	set all_databases to every database
	
	
	----Try to figure out where to put the record
	set the_Window to viewer window 1
	set win_name to get name of the_Window
	set viewer_window_record to content record of the_Window
	set viewer_current_group to current group
	set skip_record_if_it_has_one_of_these_parents to {viewer_current_group}
	
	---if no record is selected, but we have a group use that.
	if viewer_window_record is missing value then
		set viewer_window_record to current group
	end if
	
	---see if we can identify parent of current record
	try
		set viewer_window_record_parent to parent 1 of viewer_window_record
	on error
		set viewer_window_record_parent to viewer_window_record
	end try
	set viewer_window_record_location to location of viewer_window_record
	---log " viewer_window_record_parent " & ((name of viewer_window_record_parent) as string)
	
	
	
	---do the search
	set newest_record to my return_newest_record(retrospection_threshhold, all_databases, kinds_to_exclude, skip_record_if_it_has_one_of_these_parents)
	
	
	---the following line uses explicit include
	---set newest_record to my return_newest_record(retrospection_threshhold, all_databases, kinds_to_move, skip_record_if_it_has_one_of_these_parents)
	
	
	
	if newest_record is {} then
		display dialog "Sorry,but Devonthink found  no records less than " & ((retrospection_threshhold / hours) as string) & " hours old. "
		error number -128
		return
	end if
	
	log "moving to " & name of viewer_current_group
	set the_parent to parent 1 of newest_record
	set record_name to name of newest_record
	log "Moving  " & record_name & "from... " & ((name of the_parent) as string)
	my growlNotification("DEVONthink Pro", "Moving " & record_name, "from " & ((location of the_parent) & name of the_parent))
	
	move record newest_record to viewer_current_group
end tell



on return_newest_record(seconds_to_look_back, the_databases, types_searched, groups_to_ignore)
	set old_date to (current date) - (100 * days)
	set last_date_to_search to (current date) - seconds_to_look_back
	
	
	set newest_record to {}
	
	using terms from application "DEVONthink Pro"
		tell application "DEVONthink Pro"
			repeat with the_database in the_databases
				
				log "***** now searching ***" & (get name of the_database) as string
				set db_root to root of the_database
				set id_db_trash to id of trash group of the_database
				
				---set new_recs to search "" age max_age in db_root
				
				set new_recs to (contents of the_database whose addition date ≥ last_date_to_search)
				
				
				repeat with the_record in new_recs
					set exception_found to false
					log "reviewing record " & (name of the_record as string)
					set the_parents_id to id of every parent of the_record
					set ids_to_ignore to {}
					repeat with x in groups_to_ignore
						set y to get id of x
						set ids_to_ignore to ids_to_ignore & {y}
					end repeat
					set ids_to_ignore to ids_to_ignore & {id_db_trash}
					
					---log "exception list "
					---log (ids_to_ignore)
					repeat with exception_id in ids_to_ignore ---groups_to_ignore
						---set exception_id to ((get id of this_exception) as number)
						repeat with this_parent_id in the_parents_id
							---set this_parent to the first item of this_parent
							log ("exception" & exception_id as string) & "vs. parent " & this_parent_id as string
							
							if (this_parent_id as number) is equal to (exception_id as number) then
								
								set exception_found to true
								set parent_rec to get record with id exception_id in the_database
								log "parent excluded: " & (name of parent_rec as string)
								exit repeat
								
							end if
						end repeat
						if exception_found then exit repeat
					end repeat
					
					---if ((get kind of the_record)) is in types_searched and exception_found is false then
					if ((get kind of the_record) is not in types_searched) and (exception_found is false) then
						log "kind match"
						set the_name to get name of the_record
						set new_date to get addition date of the_record
						if (new_date > old_date) then
							log "Found a newer one…" & name of the_record
							set newest_record to the_record
							set old_date to new_date
						end if
					end if
				end repeat
			end repeat
			return newest_record
		end tell
	end using terms from
end return_newest_record


on growlNotification(growlIcon, growlTitle, growlDescrip)
	
	if application "GrowlHelperApp" is running then
		set appName to "ericsdtnotify"
		set notifs to {growlTitle}
		
		tell application "GrowlHelperApp"
			register as application ¬
				appName all notifications notifs ¬
				default notifications notifs ¬
				icon of application growlIcon
			notify with name growlTitle title growlTitle description growlDescrip application name appName
		end tell
	end if
	return ""
end growlNotification