How to trash only a single replicant in search results?

I found this in a 2 year old forum post: How to trash only replicants, not originals?

  • Deleted from a smart group, smart rule or search results, all instances are moved to the Trash.
  1. I’d like a warning if all replicants are deleted even if just one is selected. I recently discovered this behaviour and it was quite surprising. This would lead to data loss going unnoticed.

  2. Is there a shortcut to delete just this single replicant? Switching between views is quite cumbersome.

My use case:

  • I have a database for all my business letters and invoices.
  • For my tax return, I replicate invoices and bank statements to a group “Collected Documents” containing all the files for that return.

Structure

Screenshot translated by ChatGPT

Script

If someone is interested in the details.

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

property globalIsDev : true

tell application id "DNtp"
	activate
	
	-- get record
	local theWindow
	set theWindow to think window 1
	
	local theRecords
	set theRecords to (selection of theWindow)
	
	if length of theRecords < 1 then
		error "No selected item"
	end if
	
	local createdGroup
	set createdGroup to (item 1 of theRecords)
	
	if type of createdGroup is not group then
		error "No group selected"
	end if
	
	-- run code
	local currentYear
	set currentYear to year of (current date)
	
	local theResult
	set theResult to display dialog "Enter tax year, please" default answer currentYear
	
	local taxYear
	set taxYear to text returned of theResult
	
	local oldName
	set oldName to name of createdGroup
	
	local newName
	set newName to "für_" & taxYear
	
	set name of createdGroup to newName
	
	my dtLog(createdGroup, "INFO", "Change name of group", {"old-name=" & quoted form of oldName, "new-name=" & quoted form of newName})
	
	local baseLocation
	set baseLocation to (location with name of createdGroup)
	
	-- phase 1 group	
	local createTaxApplicationGroup
	set createTaxApplicationGroup to create location "1-Steuererklärung erstellen" in createdGroup
	
	if createTaxApplicationGroup is missing value then
		my dtLog(current group, "INFO", "Record could not be found ", {"path=" & quoted form of (location with name of createTaxApplicationGroup)})
		error "Record could not be found at " & quoted form of (location with name of createTaxApplicationGroup)
	end if
	
	local receiptsGroup
	set receiptsGroup to create location "Belege" in createTaxApplicationGroup
	
	local collectedReceiptsGroup
	set collectedReceiptsGroup to create location "Gesammelte Belege" in receiptsGroup
	
	local globalGroup
	set globalGroup to create location "/"
	
	-- create search for all tax documents in database
	
	local allTaxDocsGroupName
	set allTaxDocsGroupName to "Alle Steuerunterlagen für " & taxYear & " in DB " & name of globalGroup
	set allTaxDocsGroup to create record with {name:allTaxDocsGroupName, record type:smart group, search predicates:"kind:pdf {any: tags:taxes-" & taxYear & ";steuern-" & taxYear & ";mdtaxyear==" & taxYear & "}"} in receiptsGroup
	
	if allTaxDocsGroup is missing value then
		my dtLog(current group, "INFO", "Smart group could not be created ", {"name=" & allTaxDocsGroupName, "path=" & quoted form of location with name of createTaxApplicationGroup})
		error "Smart group " & allTaxDocsGroupName & " could not be created at " & quoted form of location with name of receiptsGroup
	end if
	
	set search group of allTaxDocsGroup to globalGroup
	
	-- create search for tags
	
	local relevantReceiptsGroup
	set relevantReceiptsGroup to create location "Relevante Belege" in receiptsGroup
	
	local houseHoldServicesSearch
	set houseHoldServicesSearch to create record with {name:"Haushaltsnahe Dienstleistungen", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-haushaltsnahe-dienstleistungen}"} in relevantReceiptsGroup
	set search group of houseHoldServicesSearch to collectedReceiptsGroup
	
	local kapSearch
	set kapSearch to create record with {name:"KAP", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-kap}"} in relevantReceiptsGroup
	set search group of kapSearch to collectedReceiptsGroup
	
	local donationSearch
	set donationSearch to create record with {name:"Spenden", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-spenden}"} in relevantReceiptsGroup
	set search group of donationSearch to collectedReceiptsGroup
	
	local devicesSearch
	set devicesSearch to create record with {name:"Beruflich genutzte Geräte", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-beruflich-genutzte-geraete}"} in relevantReceiptsGroup
	set search group of devicesSearch to collectedReceiptsGroup
	
	local booksSearch
	set booksSearch to create record with {name:"Fachliteratur", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-fachliteratur}"} in relevantReceiptsGroup
	set search group of booksSearch to collectedReceiptsGroup
	
	local ohterJobCostsSearch
	set ohterJobCostsSearch to create record with {name:"Sonstige Werbungskosten", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-werbungskosten}"} in relevantReceiptsGroup
	set search group of ohterJobCostsSearch to collectedReceiptsGroup
	
	local otherSearch
	set otherSearch to create record with {name:"Sonstiges", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-sonstiges}"} in relevantReceiptsGroup
	set search group of otherSearch to collectedReceiptsGroup
	
	-- phase 2 group
	local finishTaxApplicationGroup
	set finishTaxApplicationGroup to create location "2-Steuererklärung abgeben" in createdGroup
	
	if finishTaxApplicationGroup is missing value then
		my dtLog(current group, "INFO", "Record could not be found ", {"path=" & quoted form of (location with name of finishTaxApplicationGroup)})
		error "Record could not be found at " & quoted form of (location with name of finishTaxApplicationGroup)
	end if
	
	-- create search for kap etc.
	local submitGroup
	set submitGroup to create location "Einzureichende Belege" in finishTaxApplicationGroup
	
	local houseHoldServicesSearch
	set houseHoldServicesSearch to create record with {name:"Haushaltsnahe Dienstleistungen", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-haushaltsnahe-dienstleistungen}"} in submitGroup
	set search group of houseHoldServicesSearch to collectedReceiptsGroup
	
	local noTagsSearch
	set noTagsSearch to create record with {name:"Kein Steuer-Etikett zugeordnet", record type:smart group, search predicates:"kind:pdf tags!=steuern-belege-kap;steuern-belege-beruflich-genutzte-geraete;steuern-belege-haushaltsnahe-dienstleistungen;steuern-belege-fachliteratur;steuern-belege-sonstiges;steuern-belege-werbungskosten;steuern-belege-spenden"} in submitGroup
	set search group of noTagsSearch to collectedReceiptsGroup
	
	local kapSearch
	set kapSearch to create record with {name:"KAP", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-kap}"} in submitGroup
	set search group of kapSearch to collectedReceiptsGroup
	
	local donationSearch
	set donationSearch to create record with {name:"Spenden", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-spenden}"} in submitGroup
	set search group of donationSearch to collectedReceiptsGroup
	
	local devicesSearch
	set devicesSearch to create record with {name:"Werbungskosten", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-beruflich-genutzte-geraete;steuern-belege-fachliteratur;steuern-belege-werbungskosten}"} in submitGroup
	set search group of devicesSearch to collectedReceiptsGroup
	
	local devicesSearch
	set devicesSearch to create record with {name:"Gesundheit", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-gesundheit}"} in submitGroup
	set search group of devicesSearch to collectedReceiptsGroup
	
	local otherSearch
	set otherSearch to create record with {name:"Sonstiges", record type:smart group, search predicates:"kind:pdf {any: tags:steuern-belege-sonstiges}"} in submitGroup
	set search group of otherSearch to collectedReceiptsGroup
end tell

on dtLog(theRecord, level, msg, msgInfo)
	if class of msgInfo is not list and msgInfo is not missing value then
		error "Invalid definition of msgInfo in dtLog: Needs to be {}"
	end if
	
	if msgInfo is missing value then
		set logMsg to "msg=" & msg
	else
		set logMsg to "msg=" & msg & " " & (join strings msgInfo using delimiter return)
	end if
	
	if level is not "DEBUG" then
		tell application id "DNtp"
			if theRecord is missing value then
				log message "level=INFO " & logMsg
			else
				log message record theRecord info "level=INFO " & logMsg
			end if
		end tell
		
		return
	end if
	
	if globalIsDev is true then
		tell application id "DNtp"
			if theRecord is missing value then
				log message "level=INFO " & logMsg
			else
				log message record theRecord info "level=INFO " & logMsg
			end if
		end tell
	end if
end dtLog
  1. I’d like a warning if all replicants are deleted even if just one is selected. I recently discovered this behaviour and it was quite surprising. This would lead to data loss going unnoticed.

I think your issue might lie in your understanding of smart groups. Smart groups don’t actually “contain” any files, they’re basically just a type of advanced search that find files that meet your criteria (the criteria you define for that smart group). Deleting files from a smart group (or search) isn’t an action to remove them from your search - it’s an action to remove them from your database. As far as I can recall, this is true for all search functions in apps (you’re never removing items from a search list, you’re always deleting them from the app/file system? It’s normal behaviour :thinking:).

So in your example you’ve done a search that’s identified a file, told the system to delete it, and now want the system to tell you that you’ve just done that.

Trash indicates if the file is being deleted - if the file is struck through in the trash it means the file and all occurrences of it are being deleted. If it’s not struck through it means there’s still an occurrence of it in the database.

  1. Is there a shortcut to delete just this single replicant? Switching between views is quite cumbersome.

See above. I think your understanding of smart groups is causing confusion. It’s a search. How will DT know that you’d like a specific replicant deleting unless you’re clicking on that specific replicant?

So in your example you’ve done a search that’s identified a file, told the system to delete it, and now want the system to tell you that you’ve just done that.

Sure, they are an advanced search. But there’s an location for that file given in the search list, pointing to the group containing that replicant.

What’s the difference between delete a “file” from a group A rather from a search in that group A? I did not search for that file in the whole database.

1 Like

I don’t know, I think a DT dev would need to comment on the logic here. It makes sense to me so I guess I have an assumption/expectation that you don’t, but I don’t know how to explain it.

I guess maybe it’s that a replicant only occurs once in a database, it’s just mirrored each time you create a replicant. So if I deleted a replicant from a search, I would assume DT is removing the file from the database (how would DT know I mean “delete this specific mirror” instead of “delete this file”, when I’m not looking at the “mirror” but a search result?).

I decided to look at how the menus handle this, and the right click menu in a search only gives the option to send all occurrences to the trash - there isn’t a menu option to only delete this specific replicant, possibly because the search index is only looking at “one file”.

(Interesting quirk here @BLUEFROG : if I search a specific group, then click “Reveal” on a replicant in the results, it’s taking me to the first occurrence of the file in the database, not the replicant within the group I’ve searched. This seems unexpected behaviour? But I’m guessing is related to the point I skirt round above that the search index is only indexing one version of the file?)

I decided to look at how the menus handle this, and the right click menu in a search only gives I decided to look at how the menus handle this, and the right click menu in a search only gives

Thanks for mentioning the menus. I forgot to mention how I delete the replicant. For this task I just used the keyboard not the menus. The menu entries clearly indicate what gets deleted.

Search results and smart groups/rules do not distinguish instances separately. You are looking at one item that represents all instances of a replicant. This is why you see Move all instances to Trash at the bottom of the context menu for an item in the search results…

Super, thank you! That answers both of us :blush:

That’s not the case, if I open the context menu for a smart group record.

Development wil have to look at that but the effect is the same.

The next release will fix this glitch.

Bluefrog: I was looking for the answer to the question I was going to post about and I’ve found a partial answer with your reply.

Example scenario: I execute a search on a keyword and DT returns a list of 100 files it found the search term in. I select one of those files that I want to delete and I get the menu that you showed in your screenshot. The words “Move All Replicants to Trash” scare the hell out of me because I have quite a few replicants for files scattered throughout my databases. Does this option apply ONLY to the file that I have selected in the search results? The file I’m selecting does NOT have a replicant. If it did, the wording would make sense - delete the original and it also deletes the replicant of the original. If I want to delete the file, I have to go to its group and delete it there, not from the search list. I did read the DT manual and it says nothing about my scenario. If “Move ALL Replicants to Trash” applies only to the selected file, whether or not it has a replicant, I would be willing to try it out.

Clarification for this dummy would be greatly appreciated.

Yes, Move All Replicants to Trash is shown even if the selected search result has no replicants. In that case it just moves the singleton record to the Trash, easily distinguished by the filename now being struck through.

That being said, I think this is a bug or unexpected and it should only reference replicants when selected items are replicated. And if it’s a mix of replicants and non-replicants, it’s on the user to be thinking about that.

bluefrog:

It’s been a year now and I’m still learning about the wonderful features of DT. You’ve been a great help and I appreciate it.

Thanks for the encouragement. :heart:

By the way, I have been using it for 14 years and still learn things about it :smiley: