File Sort-O-Matic

Hi, I’m a noob, so don’t eat me. A great guy named MAtt was helping we with this script, but I’ve really asked way too much of him, so I’m bringing this here. I’m trying to write a script that will sort my image files into folders, down through the hierarchy.

I’ve included a link to a photo, which I hope shows my intent.
This is what we have so far:

property firstfolderchars : ("0" as string) --This is added to the beginning of all folder names 

on open itemsToSort
	set scriptedFolder to choose folder with prompt "In which folder do you want your sorted folders?"
	processitems(itemsToSort, scriptedFolder)
end open

on adding folder items to scriptedFolder after receiving itemsToSort
	processitems(itemsToSort, scriptedFolder)
end adding folder items to

on run
	set itemsToSort to choose file with prompt "Which files would you like to sort into sorted folders?" with multiple selections allowed
	set scriptedFolder to choose folder with prompt "In which folder do you want your sorted folders?"
	processitems(itemsToSort, scriptedFolder)
end run

on processitems(itemsToSort, scriptedFolder)
	if (length of firstfolderchars) is greater than 220 then
		set firstfolderchars to (characters 1 through 220) of firstfolderchars
	end if
	
	tell application "Finder"
		set potentialproblem to ((scriptedFolder as string) & firstfolderchars & firstfolderchars) --check for file named equal to (firstfolderchars & firstfolderchars) 
		
		if ((file potentialproblem exists) and (kind of (info for file potentialproblem) is not "folder")) then
			try
				set name of file potentialproblem to (firstfolderchars & firstfolderchars & "qwertyasdfgzxcvb")
			on error renameerror
				display dialog renameerror buttons {"okay then"} default button 1
				exit repeat
			end try
		end if
		
		
		repeat with current_item in itemsToSort
			set this_item to (info for current_item)
			if (kind of this_item is not "folder" and this_item is visible) then
				set targetfolder to (firstfolderchars & first character of (name of this_item))
				if (exists folder targetfolder of scriptedFolder) is false then
					try
						make new folder at scriptedFolder with properties {name:targetfolder}
						
					on error makefoldererror
						display dialog renameerror buttons {"okay then"} default button 1
						exit repeat
					end try
				end if
				
				set filetodelete to ((scriptedFolder as string) & (targetfolder as string) & ":" & (name of this_item))
				if (exists file filetodelete) is true then
					try
						delete filetodelete
					on error
						display dialog "Could not delete file \"" & (name of this_item) & "\" of " & (name of (info for targetfolder)) buttons {"okay then"} default button 1
						exit repeat
					end try
				end if
				
				try
					move current_item to folder targetfolder of scriptedFolder
					
				on error
					display dialog "Could not move \"" & (name of this_item) & "\"" buttons {"okay then"} default button 1
					exit repeat
				end try
			end if
		end repeat
		
		set filetochangenameback to ((scriptedFolder as string) & (targetfolder as string) & ":" & (firstfolderchars & firstfolderchars & "qwertyasdfgzxcvb"))
		if (exists file filetochangenameback) then
			try
				set name of file filetochangenameback to (firstfolderchars & firstfolderchars)
			on error renameerror2
				display dialog renameerror2 buttons {"okay then"} default button 1
				exit repeat
			end try
		end if
		
	end tell
end processitems

Now, I don’t need the script to make new folders, just dump my image files into a folder, check that folcer to see if there is another folder that the image should go further down into, and then when it sees there is no more folders to go into, stop and move on to the next image. Sounds simple, but it’s been really hard to explain. I just want the files to be filed away automatically when I save them into that first folder. It works pretty great now, but it needs to be better if I’m going to put it to work.

It also has to be generic, so I can use it on any file thats’ name starts with numbers.

Gads, I’m getting more confused the more I talk…
Help! Please! Thank you!

You say “It works pretty great now…” so what is your specific question? What are you asking for help with?

I don’t need the script to make new folders like it does, just dump my image files into a folder, check that folder to see if there is another folder inside that the image should go further down into, and then when it sees there is no more folders to go into, stop there and move on to the next image. The image I linked to should help, no?

Ok, so I’m racking my brain to figure out how to make this script… Matt tried to help me, but I thad trouble telling him what I wanted to do, so I started my own thread on the damn thing.

I want this script to file away my images for me, automagically, and I want to attach it to many folders, so it will “drill down” and kput them away into all the sub-folders.

Here’s what Matt came up with for me:


property firstfolderchars : (“0” as string) --This is added to the beginning of all folder names

on open itemsToSort
set scriptedFolder to choose folder with prompt “In which folder do you want your sorted folders?”
processitems(itemsToSort, scriptedFolder)
end open

on adding folder items to scriptedFolder after receiving itemsToSort
processitems(itemsToSort, scriptedFolder)
end adding folder items to

on run
set itemsToSort to choose file with prompt “Which files would you like to sort into sorted folders?” with multiple selections allowed
set scriptedFolder to choose folder with prompt “In which folder do you want your sorted folders?”
processitems(itemsToSort, scriptedFolder)
end run

on processitems(itemsToSort, scriptedFolder)
if (length of firstfolderchars) is greater than 220 then
set firstfolderchars to (characters 1 through 220) of firstfolderchars
end if

tell application "Finder"
	set potentialproblem to ((scriptedFolder as string) & firstfolderchars & firstfolderchars) --check for file named equal to (firstfolderchars & firstfolderchars)
	
	if ((file potentialproblem exists) and (kind of (info for file potentialproblem) is not "folder")) then
		try
			set name of file potentialproblem to (firstfolderchars & firstfolderchars & "qwertyasdfgzxcvb")
		on error renameerror
			display dialog renameerror buttons {"okay then"} default button 1
			exit repeat
		end try
	end if
	
	
	repeat with current_item in itemsToSort
		set this_item to (info for current_item)
		if (kind of this_item is not "folder" and this_item is visible) then
			set targetfolder to (firstfolderchars & first character of (name of this_item))
			if (exists folder targetfolder of scriptedFolder) is false then
				try
					make new folder at scriptedFolder with properties {name:targetfolder}
					
				on error makefoldererror
					display dialog renameerror buttons {"okay then"} default button 1
					exit repeat
				end try
			end if
			
			set filetodelete to ((scriptedFolder as string) & (targetfolder as string) & ":" & (name of this_item))
			if (exists file filetodelete) is true then
				try
					delete filetodelete
				on error
					display dialog "Could not delete file \"" & (name of this_item) & "\" of " & (name of (info for targetfolder)) buttons {"okay then"} default button 1
					exit repeat
				end try
			end if
			
			try
				move current_item to folder targetfolder of scriptedFolder
				
			on error
				display dialog "Could not move \"" & (name of this_item) & "\"" buttons {"okay then"} default button 1
				exit repeat
			end try
		end if
	end repeat
	
	set filetochangenameback to ((scriptedFolder as string) & (targetfolder as string) & ":" & (firstfolderchars & firstfolderchars & "qwertyasdfgzxcvb"))
	if (exists file filetochangenameback) then
		try
			set name of file filetochangenameback to (firstfolderchars & firstfolderchars)
		on error renameerror2
			display dialog renameerror2 buttons {"okay then"} default button 1
			exit repeat
		end try
	end if
	
end tell

end processitems


Now, this works pretty great, but there are two things wrong with the way it runs,

  1. I don’t need to make new folderss (they should already be there)
  2. I want it check all the numbers in the filenamee so it can go further down the file path into deeper folders - BUT!-
    I want it to be generic enough that I can attach it to any folder …make sense?

Here’s a photo to show what I’m too daft to say in English:

Here’s another image to clear things up.

The first step in solving a problem is to have a really clear idea of what is to be accomplished. Is this it?

  1. You want a script (which could be a folder action or a stand-alone application) that will take the files contained in a designated folder and move them down a hierarchy of folders within that (or another?) folder according to the sequential digits in their name.

  2. The first digit in a folder name is (or can be?) zero, and that should be ignored in the placement of files which do not have leading zeros in their names.

  3. The script should not have any preconceived notion of what the script and folder numbering might be – i.e. generic, but it will have to check that the files match the numbering on the folders provided for the sort. If a file name doesn’t fit the structure of the nested folders it will be left where it is (or moved somewhere else?)

  4. Until the script for doing this is thoroughly tested from the Script Editor starting with a “choose folder” statement for the target, there’s no need to worry about final packaging.

This doesn’t sound hard to do, if this is what you want to do. If there are any more rules, please state them one at a time.

#1. Yes! :slight_smile: But I suppose if I want it to be “generic”, it should only move the files “one level”…meaning it works like it does now, stopping after it places the file in one folder.

**I will be attaching this script to each subfolder that I want to apply the sort function. I will need to “turn it on and off” (disable/enable folder actions) when the need arises.

#2. Yes! I would like it to ignore a “first 0” in the folder name, if one comes up (some collegues use “02” and some just “2”, and it must work on both the same way.)

#3. Yes! And if the filename doesn’t begin with numbers, it should be ignored completely. (ex: typical file names will be “45685b.jpg, 89888.eps, 26482bfl.jpg, 231bt.jpg”)

#4. I have no idea what any of this means. “final Packaging” ? (Sorry, I’m a noob.)

Thank you very much! :slight_smile:

No reason for that. Assuming a hierarchy of folders exists, the first few digits (other than 0) will match some folder name, and you just keep trying more digits until you run out of matches then put it in the last one that did (not the first one that does).

I doubt that that is a good idea - it’s hard to manage. What I have in mind looks at the file names, looks at ALL the folder names and moves the file as I explained above.

Not a problem.

Ignoring files that don’t start with a number is simple to do, and the extension doesn’t matter either if we’re testing the name from its first character.

All I meant is that it’s best to start with a script in the Script Editor and run it from there. When it runs as you like it then you can decide how to best deploy it. I’ll have a further look tomorrow, if someone doesn’t beat me to it.

Question: I’ve made a test folder with two sub-levels of numbering, i.e. in Test Folder, 01 contains 011, 012…, 019. Are you likely to go further than that in your file sorting?

Adam

Thanks, Adam! I’m really hoping that I will learn how to do all this myself someday soon. I want to get good at it, but no matter how long I stare at the code, I can’t figure out what part is doing what! Sigh. This old dog is having trouble with his new tricks. (Is anyone still using NBASIC?) :wink: Think you comment out the code a bit so I can learn this?

Answer to your question: I won’t ever be going more than three or four folders deep. It’d be nice if it were scalable, but hey, haven’t I asked for enough already? :slight_smile: As I was saying, I’m not the only one that messes with this stuff, so it not only has to be fool proof, but multi-fool proof! ha ha.

Do you have file names with internal zeros: 840327.jpg?

Yes, frequently there are zeros within the name. Never at the beginning, however.

Here’s a first go to get you started on the learning curve. It is by no means optimized.


-- First draft - by no means optimized in any way...., but it works.
set Nest to (text returned of (display dialog "How many \"Layers\" of folders in the target folder?" & return & return & "This corresponds to the number of digits in the most deeply buried folder, not counting leading zeros, i.e. \"0345\" is 3" default answer "3" with title "Depth of File System" with icon 2)) as integer
set tSource to alias ((path to desktop as text) & "SortFiles:Source Folder:")
set tDest to alias ((path to desktop as text) & "SortFiles:Target Folder")
-- Now get the folders for sorting the files in tDest
tell application "Finder"
	set tFldrs to folders of entire contents of tDest as alias list
	-- get the names while we're at it
	set NFldrs to name of folders of entire contents of tDest
	set k to 1
	repeat with aFldr in NFldrs
		set OK to my testName(item k of NFldrs) -- this tests first letters and zeros
		if OK is false then -- starts with a letter, remove them from lists
			removeItem(NFldrs, k)
			removeItem(tFldrs, k)
		else
			set item k of NFldrs to OK
		end if
		set k to k + 1
	end repeat
	-- Now, get the files to be moved
	try
		set tFiles to files of (entire contents of tSource) as alias list -- for more than one
		-- and their names too
		set FNames to name of files of entire contents of tSource
	on error -- necessary because of an AppleScript bug - alias list doesn't work for one item
		set tFiles to files of (entire contents of tSource) as alias as list -- for only one
		-- and name of the single as a list
		set FNames to name of files of (entire contents of tSource)
	end try
end tell
-- process them one at a time
repeat with k from 1 to count FNames -- a list, even if only one.
	set GoodName to my testName(item k of FNames)
	if GoodName is not false then set T to text 1 thru Nest of GoodName
	set W to my findFolder(T, NFldrs)
	if W ≠ 0 then tell application "Finder" to move item k of tFiles to item W of tFldrs
end repeat

to findFolder(tNum, tList)
	repeat with k from 1 to count tList
		if item k of tList is tNum then return k
	end repeat
	return 0
end findFolder

to testName(N)
	try
		character 1 of N as integer -- errors if first character is in the alphabet
		if character 1 of N is not "0" then
			return N
		else
			return text 2 thru -1 of N --  discard the zero (TID = "")
		end if
	on error
		return false
	end try
end testName

to removeItem(alist, num)
	set C to count alist
	tell alist
		if num > 1 and num < C then
			return items 1 thru (num - 1) & items (num + 1) thru -1
		else if num = 1 then
			return items 2 thru -1
		else if num = C then
			return items 1 thru -2
		end if
	end tell
end removeItem

Ah! Brain food! Thanks, I’ll read over this tomorrow, if I get some time, and try to glean some intellect! :slight_smile: I’m going to need lots of luck! :slight_smile:

You should know too, IFM2, that it’s relatively easy to automate the choice of file depth and that it doesn’t really have to be uniform either (i.e., one set might go to 3 digits while another was only two). Implementing that just adds time to the task. You haven’t said much about the target file system “rules” so I made it uniform, 3 deep, for my tests.

Here’s a version that tests to see if there is an appropriate end folder and if not moves up the hierarchy. If no folder is found, the file is not moved.


-- First draft - by no means optimized in any way...., but it works.
set Nest to (text returned of (display dialog "How many \"Layers\" of folders in the target folder?" & return & return & "This corresponds to the number of digits in the most deeply buried folder, not counting leading zeros, i.e. \"0345\" is 3" default answer "3" with title "Depth of File System" with icon 2)) as integer
set tSource to alias ((path to desktop as text) & "SortFiles:Source Folder:")
set tDest to alias ((path to desktop as text) & "SortFiles:Target Folder")
-- Now get the folders for sorting the files in tDest
tell application "Finder"
	set tFldrs to folders of entire contents of tDest as alias list
	-- get the names while we're at it
	set NFldrs to name of folders of entire contents of tDest
	set k to 1
	repeat with aFldr in NFldrs
		set OK to my testName(item k of NFldrs) -- this tests first letters and zeros
		if OK is false then -- starts with a letter, remove them from lists
			removeItem(NFldrs, k)
			removeItem(tFldrs, k)
		else
			set item k of NFldrs to OK
		end if
		set k to k + 1
	end repeat
	-- Now, get the files to be moved
	try
		set tFiles to files of (entire contents of tSource) as alias list -- for more than one
		-- and their names too
		set FNames to name of files of entire contents of tSource
	on error -- necessary because of an AppleScript bug - alias list doesn't work for one item
		set tFiles to files of (entire contents of tSource) as alias as list -- for only one
		-- and name of the single as a list
		set FNames to name of files of (entire contents of tSource)
	end try
end tell
-- process them one at a time
repeat with k from 1 to count FNames -- a list, even if only one.
	set GoodName to my testName(item k of FNames)
	if GoodName is not false then set T to text 1 thru Nest of GoodName
	set Tst to true
	repeat
		set W to my findFolder(T, NFldrs)
		if W = 0 then
			if Nest - 1 = 0 then
				set Tst to false
				exit repeat
			end if
			set T to text 1 thru (Nest - 1) of GoodName
		else
			exit repeat
		end if
	end repeat
	if Tst then tell application "Finder" to move item k of tFiles to item W of tFldrs
end repeat

to findFolder(tNum, tList)
	repeat with k from 1 to count tList
		if item k of tList is tNum then return k
	end repeat
	return 0
end findFolder

to testName(N)
	try
		character 1 of N as integer -- errors if first character is in the alphabet
		if character 1 of N is not "0" then
			return N
		else
			return text 2 thru -1 of N --  discard the zero (TID = "")
		end if
	on error
		return false
	end try
end testName

to removeItem(alist, num)
	set C to count alist
	tell alist
		if num > 1 and num < C then
			return items 1 thru (num - 1) & items (num + 1) thru -1
		else if num = 1 then
			return items 2 thru -1
		else if num = C then
			return items 1 thru -2
		end if
	end tell
end removeItem

Cool, I’ll give it a try. I heard somewhere that it’s better to copy/past/remove files than to move, as you can lose your data if the stream is interrupted(power failure, etc.)…is this true?

The method I’d advise if you’re worried is to duplicate the file, check that it’s OK by comparing checksums, and then delete. If the Finder is used for the deletion, the files end up in the trash. If not, they disappear.

If you try to move a file from one volume to another (between partitions of a disk, to another disk, or to another machine) it is copied anyway, like it or not, and the original remains in place.

Checking integrity uses a Unix tool called md5.


set tFile to quoted form of POSIX path of (choose file) -- or any alias to a file.
set CkSum to last word of (do shell script "md5 " & tFile) -- a big hex number like "da64a967054842824eb8ccad5b42931e"
-- do the duplication, then get the CS of the new file, compare to the old, then delete the old.