Batch convert files recursively

Hey all,

I’m not new to programming, but I am new to AppleScript. I’d like some help debugging this script which is meant to have a folder dragged onto it to execute. The file then recursively opens each file and saves a copy of the file as a Pages document within the same directory.

on open theFiles
	tell application "Pages"
		repeat with aFile in theFiles
			if aFile ends with ".cwk" then
				open aFile
				set docName to name of front document
				-- Remove .cwk extension.
				set prevTIDs to AppleScript's text item delimiters
				set AppleScript's text item delimiters to ".cwk"
				-- Add .pages extension.
				set docName to first text item of docName & ".pages"
				set AppleScript's text item delimiters to prevTIDs
				-- Get folder that dropped file exists in.
				tell application "Finder"
					set sourceFolder to (container of aFile) as Unicode text
				end tell -- Finder
				-- Save file to folder that dropped file exists in.
				set docPathAndName to sourceFolder & docName
				save front document in docPathAndName
				close front document
			end if
		end repeat
	end tell
end open

You are starting on wrong basis.

(1) AppleWorks file names aren’t always ending with “.cwk”
(2) iWork apps aren’t able to open every AppleWorks files, only those created by AppleWorks 6.x
(3) Pages isn’t able to open every AppleWorks 6 documents, only those whose kind is Word Processor.

There is no way to identify these files using only documented attributes.

(a) As written above the name extension isn’t always available.
(b) the file type and creator type properties aren’t always available
(c) under Lion, the type identifier isn’t defined so we get something like “dyn.agk8ygz41na”
(d) when they are available, these attributes don’t allow us to make the difference between AW6 documents and documents created with older versions.

This is was, for years, I delivered scripts relying upon undocumented attributes.

You may find them in my public BOX account :
http://www.box.com/s/00qnssoyeq2xvc22ra4k

download :
public_YK > for_iWork > batch_AWx2iWork.zip

Yvan KOENIG (VALLAURIS, France) mardi 3 avril 2012 09:57:31

Thanks for the reply.

You need me to clarify some points so you can provide me with better feedback I think.

  1. all the files end in .cwk
  2. all files were created with version 6+
  3. all files are word processing files

You’ve lost me on why I need to delve into document attributes here.

Thanks Yvan Koenig. I can report that I have successfully implemented your script with a slight modification.

I did not want to delete the existing file from the directory and place it in a zip file, so I have removed those steps. It was otherwise very robust for my purposes! Thanks!!!

--[SCRIPT batch_AW6_2iWork]
(* 
Enregistrer ce script en tant qu'application.
Exécuter ce script ou déposer 
l'icône d'un dossier sur son icône.
Il importe les tableurs et traitement de texte AppleWorks 6 du dossier dans Numbers ou Pages
et enregistre les documents Numbers  ou Pages créés
dans le dossier source (respectant l'arborescence).

Les anomalies éventuelles sont répertoriées dans le fichier  
"import2iWorkReport.txt" déposé sur le bureau

Activités annexes :
(1) le dossier source est préservé dans une archive zip.
(2) les fichiers de type vectoriel (CWGR) sont préservés dans un sous-dossier dont le nom est défini par la property nom_GR.
(3) les autres fichiers qui ne peuvent être traités sont préservés dans un sous-dossier dont le nom est défini par la property nom_alt.
(4) les fichiers originaux qui ont pu être exportés sont effacés.
(5) Le script ne peut hélas pas se prémunir d'un éventuel blocage durant l'importation dans Numbers ou Pages. Dans ce cas, forcer le redémarrage est la seule issue disponible.

--=====

Save the script as application.
Run it or drag and drop a folder icon on its icon.
It import AppleWorks 6 SS or WP files stored in the folder in Numbers or Pages
and save the created Numbers or Pages document
in the source folder (including original hierarchy).

Errors are reported in the file 
"import2iWorkReport.txt" stored on the desktop.

Complementary tasks :
(1) the source folder is archived in a zip file.
(2) the Draw files (CWGR) are stored in a subfolder whose name is defined by the property nom_GR
(3) the other files which can't be treated are stored in a subfolder whose name is defined by the property nom_alt
(4) the original files which were exported are deleted
(5) Alas, the script can't recover cases where Numbers or Pages freeze during an import process. Reboot is the unique emergency issue available.

Yvan KOENIG (VALLAURIS, France) 
2008/02/07
2008/04/28
2009/01/25
2011/10/06
2011/10/07
2011/10/09 -- we may drop file(s) or folder(s) on the script's icon
2011/10/13 -- don't treat old AW WP or SS
*)

property srcTypeId : "com.apple.appleworks.cwk"
property v6 : " [v6.0]" -- attention à l'espace de tête
property nomDuRapport : "" -- globale
property nom_GR : "" -- globale
property nom_alt : "" -- globale

property theApp : "" -- globale
property theExt : "" -- globale
property rapport : "" -- globale
property dossierDeStockage : "" -- globale

--=====

on run (* lignes exécutées si on double clique sur l'icône du script application
¢ lines executed if one double click the application script's icon *)
	activate
	(choose folder) as text
	my main({result})
end run

--=====

on open (sel) (* sel contient une liste d'alias des éléments qu'on a déposés sur l'icône du script (la sélection)
¢ sel contains a list of aliases of the items dropped on the script's icon (the selection) *)
	my main(sel)
end open

--=====

on main(les_originaux)
	local dest, p2d, p2r, MsgErr, NroErr
	my nettoie()
	
	if my parleAnglais() then
		set nomDuRapport to "report_AW2iWork.txt"
		set nom_GR to "Draw"
		set nom_alt to "Others"
		my afficheLeMessage("Don't click when the script is running." & return & "Except, of course, if it ask for.")
	else
		set nomDuRapport to "rapport_AW2_vers_Work.txt"
		set nom_GR to "Vectoriel"
		set nom_alt to "Autres"
		my afficheLeMessage("Éviter de cliquer durant l'exécution du script" & return & "sauf s'il le demande.")
	end if
	try
		repeat with un_original in les_originaux
			set un_original to un_original as text
			if un_original ends with ":" then
				set dest to quoted form of POSIX path of ((text 1 thru -2 of un_original) & ".zip")
			else
				set dest to quoted form of POSIX path of (un_original & ".zip")
			end if
			(*
Archive the source folder in a zip file *)
			--do shell script "ditto -ck " & quoted form of POSIX path of un_original & " " & dest
			(*
Enter the main loop *)
			
			try
				my exploreTraite(un_original as alias, "")
			end try
		end repeat
		if rapport = "" then
			if my parleAnglais() then
				set rapport to "No problem during the recoding process."
			else
				set rapport to "Conversion réussie sans incident."
			end if
		end if
		(*
Create a report text file on the Desktop *)
		set p2d to path to desktop
		set p2r to "" & p2d & nomDuRapport
		tell application "System Events"
			if exists file p2r then delete file p2r
			make new file at end of p2d with properties {name:nomDuRapport}
		end tell
		write (rapport as text) to file p2r
		
	on error MsgErr number NroErr
		if NroErr is not -128 then
			beep 2
			activate
			if my parleAnglais() then
				display dialog "" & NroErr & " : " & MsgErr with icon 0 buttons {"Oops"} giving up after 20
			else
				display dialog "" & NroErr & " : " & MsgErr with icon 0 buttons {" Vu "} giving up after 20
			end if
		end if -- NroErr is.
		return
	end try
	my nettoie()
	if my parleAnglais() then
		my afficheLeMessage("Process is done!")
	else
		my afficheLeMessage("Traitement terminé !")
	end if
end main

--============= 

on afficheLeMessage(m)
	beep 1
	activate
	if my parleAnglais() then
		display dialog m buttons {" OK "} default button 1 giving up after 10
	else
		display dialog m buttons {" Vu "} default button 1 giving up after 10
	end if
end afficheLeMessage

--===========

on exploreTraite(elem, ptree) (*
elem est un alias
¢ elem is an alias *)
	local elem_, laClasse, flag
	
	set elem_ to elem as text
	tell application "System Events" to tell disk item elem_
		set {origName, laClasse, dossierDeStockage} to {name, class as text, path of container}
	end tell
	if laClasse is in {"file package", "«class cpkg»"} then (*
Attention, un package EST un dossier "spécial".
¢ Caution, a package IS a "special" folder. *)
		if my parleAnglais() then
			set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " is a Package" & return
		else
			set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " est un Package" & return
		end if
		my |déménage|(nom_alt, elem_)
	else if laClasse is in {"folder", "«class cfol»"} then (*
traitement des dossiers à l'exclusion de ceux que le script crée.
¢ treat the folders minus those created by the script itself. *)
		if origName is not in {nom_GR, nom_alt} then my ExploreUnDossier(elem_, ptree)
	else if my testStuffit(elem) is true then (*
Rejette fichier Stuffit déguisé en AppleWorks. 
¢ Skip Stuffit file disguised as AppleWorks *)
		if my parleAnglais() then
			set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " is a Stuffit file" & return
		else
			set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " est un fichier Stuffit" & return
		end if
		my |déménage|(nom_alt, elem_)
	else
		set flag to my testeAWsignature(elem)
		
		if theApp is in {"Pages", "Numbers"} then
			if flag = 4 then (*
C'est un Tableur ou un Tdt AppleWorks 6.
¢ It's an AppleWorks 6 SS or WP *)
				my AW6to_iWork(elem_)
			else if flag = 2 then (*
C'est un "vieux" Tableur ou Tdt AppleWorks.
¢ It's an "old" AppleWorks SS or WP *)
				if my parleAnglais() then
					if theApp is "Pages" then
						set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " is an old AW WP file which can't be exported" & return
					else
						set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " is an old AW SS file which can't be exported" & return
					end if
				else
					set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " est un ancien fichier AW non exportable" & return
				end if
				my |déménage|(nom_alt, elem_)
			else if flag = 3 then
				(*
Logically we don't come here *)
				if my parleAnglais() then
					set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " is an AW6 file which can't be exported" & return
				else
					set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " est un fichier AW6 non exportable" & return
				end if
				my |déménage|(nom_alt, elem_)
			else if flag = 1 then
				(*
Logically we don't come here *)
				if my parleAnglais() then
					set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " is an old AW file which can't be exported" & return
				else
					set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " est un ancien fichier AW non exportable" & return
				end if
				my |déménage|(nom_alt, elem_)
			else
				(*
Logically we don't come here *)
				if my parleAnglais() then
					set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " isn't an AW file" & return
				else
					set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " n'est pas un fichier AW" & return
				end if
				my |déménage|(nom_alt, elem_)
			end if -- flag.
		else if theApp is "-" then
			(*
¢ It's an AW Draw file *)
			if my parleAnglais() then
				set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " is an AW Draw file" & return
			else
				set rapport to rapport & ">>> " & dossierDeStockage & nom_alt & ":" & origName & " est un fichier vectoriel AW" & return
			end if
			my |déménage|(nom_GR, elem_)
		else
			(*
¢ It's a file which can't be treated *)
			if my parleAnglais() then
				set rapport to rapport & ">>> can't treat " & dossierDeStockage & nom_alt & ":" & origName & return
			else
				set rapport to rapport & ">>> impossible de traiter " & dossierDeStockage & nom_alt & ":" & origName & return
			end if
			my |déménage|(nom_alt, elem_)
		end if -- theApp.
	end if -- laClasse is .
end exploreTraite

--=====

on |déménage|(nom_doss, un_item)
	set reserve to dossierDeStockage & nom_doss
	tell application "System Events"
		if not (exists folder reserve) then make new folder at end of folder dossierDeStockage with properties {name:nom_doss}
	end tell
	do shell script "mv " & quoted form of POSIX path of un_item & space & quoted form of POSIX path of reserve
end |déménage|

--===========

on testeAWsignature(f)
	local flag, deb280, versionNum, marqueur
	
	set deb280 to (read f from 1 for 280)
	if (text 5 thru 8 of deb280) is "BOBO" then (* it's an  AppleWorks document *)
		set flag to 1
		set versionNum to ASCII number of (character 1 of deb280)
		if versionNum = 6 then
			set marqueur to 279 -- cas de la version AW6
			set flag to 3
		else if versionNum = 5 then
			set marqueur to 269 -- cas de la version AW5/CW5
		else if versionNum = 4 then
			set marqueur to 258 -- cas de la version CW4
		else if versionNum = 3 then
			set marqueur to 250 -- cas de la version CW3 
		else if versionNum = 2 then
			set marqueur to 250 -- cas de la version CW2
		else if versionNum = 1 then
			set marqueur to 244 -- cas de la version CW1
		else
			set marqueur to 0
		end if -- character 1 .
		(* The character at offset marqueur is a type signature
0 = CWGR
1 = CWWP
2 = CWSS
3 = CWDB
4 = CWPT
5 = CWPR
*)
		set maybe to ASCII number (character marqueur of deb280)
		if maybe > 3 then
			set {theApp, theExt, flag} to {"", "", 0}
		else
			set {theApp, theExt} to item (1 + maybe) of {{"-", "-"}, {"Pages", "pages"}, {"Numbers", "numbers"}}
			if marqueur > 0 then set flag to flag + 1 (* -> 2 ou 4 *)
		end if
	else
		set {theApp, theExt, flag} to {"", "", 0}
	end if --  (text 5 thru.
	return flag (*
	0 = pas AppleWorks
	1 = oldAW pas xxx
	2 = oldAW xxx
	3 = AW6 pas xxx
	4 = AW6 xxx *)
end testeAWsignature

--===========

on testStuffit(f)
	return (read f from 1 for 100) contains "Stuffit"
end testStuffit

--===========

on ExploreUnDossier(dossier, ptree)
	local nomElement, cheminElement
	repeat with nomElement in list folder dossier without invisibles
		try
			set cheminElement to dossier & nomElement
			tell application "Finder" to set c to name of (dossier as alias)
			my exploreTraite(cheminElement as alias, ptree & c & ":")
		on error
			(*
¢ It's a folder which can't be treated *)
			if my parleAnglais() then
				set rapport to rapport & ">>> " & dossier & " contains a file with an illegal name" & return
			else
				set rapport to rapport & ">>> " & dossier & " contient un fichier au nom incorrect" & return
			end if
			my |déménage|(nom_alt, dossier)
		end try
	end repeat
end ExploreUnDossier

--=====

on AW6to_iWork(leCheminAW6_UniText)
	local p, flag, nom_de_p, ext_de_p, nouveauNom
	
	set p to leCheminAW6_UniText as alias
	tell application "System Events" to tell disk item leCheminAW6_UniText
		set {nom_de_p, ext_de_p} to {name, name extension}
	end tell -- System Events.
	
	if ext_de_p is not "" then set nom_de_p to text 1 thru -(2 + (length of ext_de_p)) of nom_de_p
	set nouveauNom to nom_de_p & "." & theExt
	
	try (* 1 *)
		set flag to false
		tell application theApp
			try
				set oldCnt to my getNbWindows()
			on error
				set oldCnt to 0
			end try
			open p
			repeat 300 times (*
Attends que le fichier soit réellement ouvert.
¢ Wait until the file is really open *)
				if my getNbWindows() > oldCnt then
					set flag to true
					exit repeat
				end if
			end repeat
		end tell -- to theApp
		if flag is false then error number 8888
	on error
		if NroErr = 8888 then
			if my parleAnglais() then
				set rapport to rapport & ">>> " & theApp & " can't read " & leCheminAW6_UniText & return
			else
				set rapport to rapport & ">>> " & theApp & " n'a pas pu lire " & leCheminAW6_UniText & return
			end if
		else
			if my parleAnglais() then
				set rapport to rapport & ">>> " & theApp & " can't open " & leCheminAW6_UniText & return
			else
				set rapport to rapport & ">>> " & theApp & " n'a pas ouvert " & leCheminAW6_UniText & return
			end if
		end if
		my |déménage|(nom_alt, leCheminAW6_UniText)
		(*
¢ can't achieve the remaining tasks *)
		return
	end try (* 1 *)
	
	set nouveauChemin to dossierDeStockage & nouveauNom
	tell application "System Events" to if exists (disk item nouveauChemin) then set name of disk item nouveauChemin to nom_de_p & my horoDateur(modification date of disk item nouveauChemin) & "." & theExt (* name stamped *)
	try (* 2 *)
		tell application theApp
			save document 1 in nouveauChemin
			close document 1
		end tell -- theApp
		
		
		
		
		(*
		try
			tell application "System Events" to delete disk item leCheminAW6_UniText
		end try
		*)
		
		
		
		
		
	on error
		if my parleAnglais() then
			set rapport to rapport & ">>> " & theApp & " can't export " & leCheminAW6_UniText & return
			els
			set rapport to rapport & ">>> " & theApp & " n'a pas exporté " & leCheminAW6_UniText & return
		end if
		my |déménage|(nom_alt, leCheminAW6_UniText)
	end try (* 2 *)
end AW6to_iWork

--=====
(* 
¢ Build a stamp from the modification date_time
*)
on horoDateur(une_date)
	tell une_date to return "_" & (((its year) * 10000 + (its month) * 100 + (its day)) as text) & "_" & text 2 thru -1 of ((1000000 + (its hours) * 10000 + (its minutes) * 100 + (its seconds)) as text)
end horoDateur

--=====
(* 
¢ Wait that the file is completely written on disk 
*)
on wait4AWfile(p)
	local oldSize, newSize
	set oldSize to 0
	repeat
		tell application "System Events" to set newSize to physical size of file p
		if oldSize < newSize then
			set oldSize to newSize
		else
			exit repeat
		end if
	end repeat
end wait4AWfile

--=====

on getNbWindows()
	tell application "System Events" to tell (first process whose title is theApp) to return count of windows
end getNbWindows

--=====

on parleAnglais()
	return (do shell script "defaults read 'Apple Global Domain' AppleLocale") does not start with "fr_"
end parleAnglais

--===== 

on nettoie() (*
pour ne pas stocker dans le fichier script
¢ So it will not be stored in the script file *)
	set dossierDeStockage to ""
	set rapport to ""
	set theApp to ""
	set theExt to ""
	set nomDuRapport to ""
	set nom_GR to ""
	set nom_alt to ""
end nettoie

--=====

--[/SCRIPT]

Thanks

I looked back into you script.

(1) It’s not a recursive one. It’s just a simple loop which doesn’t treat files stored in subfolder of the treated folder.
(2) For tests I rebuilt it this way :


on run
	choose file with multiple selections allowed
	my doyourduty(result as list)
end run

on open theFiles
	my doyourduty(theFiles)
end open

on doyourduty(theFiles)
	
	tell application "Pages"
		repeat with aFile in theFiles
			if (aFile as text) ends with ".cwk" then
				open aFile
				set docName to name of front document
				-- Remove .cwk extension.
				set prevTIDs to AppleScript's text item delimiters
				set AppleScript's text item delimiters to ".cwk"
				-- Add .pages extension.
				set docName to first text item of docName & ".pages"
				set AppleScript's text item delimiters to prevTIDs
				-- Get folder that dropped file exists in.
				tell application "Finder"
					set sourceFolder to (container of aFile) as Unicode text
				end tell -- Finder
				-- Save file to folder that dropped file exists in.
				set docPathAndName to sourceFolder & docName
				save front document in docPathAndName
				close front document
			end if
		end repeat
	end tell
end doyourduty

With this structure, I was able to get the log report.
As the run handler get a list of files, everything work well.
This let me remember some words of your original message :
“which is meant to have a folder dragged onto it to execute”

Your code is unable to do the wanted duty if you drag a folder onto it.
It’s designed to apply if you drop on it files selected in a folder.

Yvan KOENIG (VALLAURIS, France) mercredi 4 avril 2012 10:25:55

This is a recursive version which considers also (dragged) folders


on run
	doyourduty(choose file with multiple selections allowed)
end run

on open droppedItems
	doyourduty(droppedItems)
end open

on doyourduty(theItems)
	repeat with anItem in theItems
		tell application "Finder"
			set {name:fileName, name extension:nameExtension, class:itemClass, container:parentFolder} to item (anItem as text)
			set isFolder to itemClass is folder
		end tell
		if isFolder then
			tell application "Finder" to set itemList to items of anItem
			doyourduty(itemList)
		else
			if nameExtension is "cwk" then
				tell application "Pages"
					open (anItem as alias)
					-- Save file to folder that dropped file exists in.
					set docPathAndName to (parentFolder as text) & text 1 thru -4 of fileName & "pages"
					save front document in docPathAndName
					close front document
				end tell
			end if
		end if
	end repeat
end doyourduty