collect from address book related names field one by one

Hello I am trying to collect from my address book some of my custom fields that I created. I am running mavericks.

Yvan gave me a starting help with some of his archived scripts that I modified a bit and I now can collect all related names at once but could not frigure out how to just select some.

For example how to extract from the related names fields the field “region” and it’s value?

Her is the script modified for collecting all fields.


--[SCRIPT Dialogue recherche complet]
(*
sur une idée de Yannik DUMONT

KOENIG Yvan (VALLAURIS, France)
2010/04/15
*)
--=====

on run
	if my parleAnglais() then
		set prompt to "Search."
		set bouton1 to ".by last name"
		set bouton2 to ".by first name"
		set bouton3 to ".by company"
	else
		set prompt to "Recherche."
		set bouton1 to ".par nom"
		set bouton2 to ".par prénom"
		set bouton3 to ".par entreprise"
	end if
	set |réponse| to (display dialog prompt default answer "" buttons {bouton1, bouton2, bouton3} default button 1)
	
	set valeur to text returned of |réponse| (* fait une fois pour toutes *)
	set choix to button returned of |réponse| (* fait une fois pour toutes *)
	
	if choix = bouton1 then
		tell application "Contacts"
			set lesPersonnes to people whose last name contains valeur
		end tell
		
	else if choix = bouton2 then
		tell application "Contacts"
			set lesPersonnes to people whose first name contains valeur
		end tell
		
	else
		tell application "Contacts"
			set lesPersonnes to people whose organization contains valeur
		end tell
	end if
	(*
Maintenant on a la liste des fiches
*)
	if not lesPersonnes is {} then
		set lesFiches to {}
		tell application "Contacts"
			repeat with i from 1 to count of lesPersonnes
				tell item i of lesPersonnes
					set |lePrénom| to get first name
					if |lePrénom| is missing value then set |lePrénom| to ""
					
					set leNom to get last name
					if leNom is missing value then set leNom to ""
					
					set leSuffixe to (get title)
					if leSuffixe is missing value then set leSuffixe to ""
					
					set l_Entreprise to (get organization)
					if l_Entreprise is missing value then set l_Entreprise to ""
					
					set leService to (get department)
					if leService is missing value then set leService to ""
					(*
properties of an email record :
value
label
*)
					set l_Email to {}
					if (count of email) > 0 then
						set descripteurs to every email
						if class of descripteurs is not list then
							copy value of descripteurs to end of l_Email
						else
							repeat with unDescripteur in descripteurs
								copy value of unDescripteur to end of l_Email
							end repeat
						end if
					end if
					set l_Email to my recolle(l_Email, ", ")
					(*
properties of a phone record :
value
label
*)
					set |leTéléphone| to {}
					if (count of phone) > 0 then
						set descripteurs to every phone
						if class of descripteurs is not list then
							copy value of descripteurs to end of |leTéléphone|
						else
							repeat with unDescripteur in descripteurs
								copy value of unDescripteur to end of |leTéléphone|
							end repeat
						end if
					end if
					set |leTéléphone| to my recolle(|leTéléphone|, ", ")

properties of a custom related names :
value
label
*)
					set myRelatedNames to {}
					if (count of related name) > 0 then
						set descripteurs to every related name
						if class of descripteurs is not list then
							copy ("" & value of descripteurs & ", " & label of descripteurs) to end of myRelatedNames
						else
							repeat with unDescripteur in descripteurs
								copy ("" & value of unDescripteur & ", " & label of unDescripteur) to end of myRelatedNames
							end repeat
						end if -- class of.
					end if
					
					set myRelatedNames to my recolle(myRelatedNames, ", ")

					copy my recolle({leNom, |lePrénom|, leSuffixe, l_Entreprise, leService, l_Email, |leTéléphone|, lesDatesPerso, myRelatedNames}, tab) to end of lesFiches
				end tell -- the_Person
				
			end repeat
		end tell -- AddressBook
		
		set the clipboard to my recolle(lesFiches, return)
		(*
Now datas are in the clipboard, use them as you want
*)
	end if
end run

--=====

on recolle(l, d)
	local t
	set AppleScript's text item delimiters to d
	set t to l as text
	set AppleScript's text item delimiters to ""
	return t
end recolle

--=====

on remplace(t, d1, d2)
	local l
	set AppleScript's text item delimiters to d1
	set l to text items of t
	set AppleScript's text item delimiters to d2
	set t to l as text
	set AppleScript's text item delimiters to ""
	return t
end remplace

--=====

on parleAnglais()
	local z
	try
		tell application "Contacts" to set z to localized string "CANCEL"
	on error
		set z to "Cancel"
	end try
	return (z is not "Annuler")
end parleAnglais

--=====
--[/SCRIPT]


Thanks
Claude

May you try to extract the field “State” ?

I ask that because in the resources of iWork, I discovered that the localized string for the keys “State” and “State Synonyms” is “Région/Province”

Here is an old script which was used to extract some datas from AddressBook.

AddressBook is now replaced by Contacts but the script refuse to compile.
I don’t have time available to search what is wrong.
Maybe some body will discover the wrongdoer.

At this time the handler supposed to drive Contacts is commented out in a block :
(*
.
*)

Sounds foolish but when I grabbed the script from this message and removed the (* and the *) enclosing the code of the handler add_update_record, it compiled flawlessly.
Maybe and odd control char was embedded in the original file.

#{code}
#[SCRIPT Numbers_to_AddressBook]
(*
 
Enregistrer le script en tant que Script : Numbers_to_AddressBook.scpt
déplacer le fichier créé dans le dossier
<VolumeDeDémarrage>:Users:<votreCompte>:Library:Scripts:Applications:Numbers:
Il vous faudra peut-être créer le dossier Numbers et peut-être même le dossier Applications.
 
Sélectionnez les cellules de la colonne 1 des fiches à exporter.
menu Scripts > Numbers > Numbers_to_AddressBook 
Les fiches seront copiées dans AddressBook.
 
#=====
 
L'aide du Finder explique:
L'Utilitaire AppleScript permet d'activer le Menu des scripts :
Ouvrez l'Utilitaire AppleScript situé dans le dossier Applications/AppleScript.
Cochez la case "Afficher le menu des scripts dans la barre de menus".
 
+++++++++
 
Save the script as a Script: Numbers_to_AddressBook.scpt
 
Move the newly created file into the folder:
<startup Volume>:Users:<yourAccount>:Library:Scripts:Applications:Numbers:
Maybe you would have to create the folder Numbers and even the folder Applications by yourself.
 
Select the cells of column 1 of the range of records to export.
menu Scripts > Numbers > Numbers_to_AddressBook 
The records will be copied in AddressBook.
 
#=====
 
The Finder's Help explains:
To make the Script menu appear:
Open the AppleScript utility located in Applications/AppleScript.
Select the "Show Script Menu in menu bar" checkbox.
 
#=====
 
Yvan KOENIG (VALLAURIS, France)
2009/12/01
2009/12/02 : add support of firstName  which help to take care of duplicated lastNames
2009/12/03 : add support for isItAcompany setting, for Group setting,  and skip row 1 if it was selected.
2010/07/11 : added several try/end try to get rid of possibles oddities
					 : moved a try instruction to accomodate to Leopard behaviour
*)

#=====

(* Edit these properties to fit your needs *)
property column_LastName : 2
property column_MailAddress : 9
property column_Street : 4
property column_Zip : 7
property column_City : 5
property column_URL : 1

property column_FirstName : 3
property useFirstName : true
(* true if the Numbers Document contains a firstName column
false if the Numbers Document doesn't contain a firstName column *)

property column_isItAcompany : 12
property useCompany : true
(* true if the Numbers Document contains a isItAcompany column
false if the Numbers Document doesn't contain a isItAcompany column
If this column exists, it would be fine to define cells as checkboxes.
Box checked should mean : it's a company.
If the column doesn't exist, every treated record will be defined as a Company one (script's historical legacy)
*)

property column_Group : 15
property useGroup : true
(* true if the Numbers Document contains a Group column
false if the Numbers Document doesn't contain a Group column
*)

#=====

on run
	(*
Use the properties :
column_LastName, column_MailAddress, column_Street, column_Zip, column_City, column_URL
useFirstName, column_FirstName, useCompany, column_isItAcompany, useGroup, column_Group
*)
	local dName, sName, tName, rowNum1, colNum1, rowNum2, colNum2, r
	local theLastName, theMailAddress, theStreet, theZip, theCity, theUrl, theFirstName, workFlag, groupName
	
	set {dName, sName, tName, rowNum1, colNum1, rowNum2, colNum2} to my get_SelParams()
	
	tell application "Numbers" to tell document dName to tell sheet sName to tell table tName
		# Long instruction useful when iPlay '13 is installed
		# tell application "/Applications/iWork '09/Numbers.app" to tell document dName to tell sheet sName to tell table tName
		if rowNum1 = 1 then set rowNum1 to 2 (* Always skip row 1 dedicated to fields names *)
		if rowNum2 = 1 then
			if my parleAnglais() then
				error "You selected only row 1 !" (* So we can't do the trick *)
			else
				error "Vous n'avez sélectionné que la ligne 1 !"
			end if # parleAnglais
		end if # rowNum2 .
		(*
A loop scanning selected rows
*)
		repeat with r from rowNum1 to rowNum2
			tell row r
				set theLastName to value of cell column_LastName (* grabs the last name *)
				if theLastName is not 0.0 then (* Will skip rows without a last name *)
					set theLastName to theLastName as text
					
					set theMailAddress to value of cell column_MailAddress (* grabs the mail address *)
					if theMailAddress is 0.0 then
						set theMailAddress to missing value
					else
						set theMailAddress to theMailAddress as text
					end if # theMailAddress
					#log "theMailAddress : " & theMailAddress
					set theStreet to value of cell column_Street (* grabs the street *)
					if theStreet is 0.0 then
						set theStreet to missing value
					else
						set theStreet to theStreet as text
					end if # theStreet
					
					set theZip to value of cell column_Zip (* grabs the zip code *)
					if theZip is 0.0 then
						set theZip to missing value
					else
						set theZip to theZip as text
					end if # theZip
					
					set theCity to value of cell column_City (* grabs the City name *)
					if theCity is 0.0 then
						set theCity to missing value
					else
						set theCity to theCity as text
					end if # theCity
					
					set theUrl to value of cell column_URL (* grabs the URL *)
					if theUrl is 0.0 then
						set theUrl to missing value
					else
						set theUrl to theUrl as text
					end if # theUrl
					
					if useFirstName then
						set theFirstName to value of cell column_FirstName (* grabs the first name *)
						if theFirstName is 0.0 then
							set theFirstName to missing value
						else
							set theFirstName to theFirstName as text
						end if # theFirstName
					else
						set theFirstName to missing value
					end if # useFirstName
					
					if useCompany then
						set workFlag to value of cell column_isItAcompany (* grabs the isItAcompany flag, 
the best choice would be to use a checkbox for this field *)
						set workFlag to workFlag is in {true, "true", "vrai"}
					else
						set workFlag to true (* 
Defaulting to true may appear to be illogical. I agree but it's a script's historical legacy.
It was created for a user whose every record is a company one so there was no specific data field *)
					end if
					
					if useGroup then
						set groupName to value of cell column_Group (* grabs the Group name *)
						if groupName is 0.0 then
							set groupName to missing value
						else
							set groupName to groupName as text
						end if
					else
						set groupName to missing value
					end if
					(*
Calls AddressBook passing some parameters
*)
					my add_update_record(theLastName, workFlag, theStreet, theZip, theCity, theMailAddress, theUrl, theFirstName, groupName)
				end if # theLastName .
			end tell # row r
		end repeat
	end tell # Numbers
end run

#=====
(*
set { dName, sName, tName,  rowNum1, colNum1, rowNum2, colNum2} to my get_SelParams()
tell application "/Applications/iWork '09/Numbers.app" to tell document dName to tell sheet sName to tell table tName
*)
on get_SelParams()
	local d_Name, s_Name, t_Name, row_Num1, col_Num1, row_Num2, col_Num2
	tell application "Numbers" to tell document 1
		# Long instruction useful when iPlay '13 is installed
		# tell application "/Applications/iWork '09/Numbers.app" to tell document 1
		set d_Name to its name
		set s_Name to ""
		repeat with i from 1 to the count of sheets
			tell sheet i to set maybe to the count of (tables whose selection range is not missing value)
			if maybe is not 0 then
				set s_Name to name of sheet i
				exit repeat
			end if # maybe is not 0
		end repeat
		if s_Name is "" then
			if my parleAnglais() then
				error "No sheet has a selected table embedding at least one selected cell !"
			else
				error "Aucune feuille ne contient une table ayant au moins une cellule sélectionnée !"
			end if
		end if
		tell sheet s_Name to tell (first table where selection range is not missing value)
			tell selection range
				set {top_left, bottom_right} to {name of first cell, name of last cell}
			end tell
			set t_Name to its name
			tell cell top_left to set {row_Num1, col_Num1} to {address of its row, address of its column}
			if top_left is bottom_right then
				set {row_Num2, col_Num2} to {row_Num1, col_Num1}
			else
				tell cell bottom_right to set {row_Num2, col_Num2} to {address of its row, address of its column}
			end if
		end tell # sheet.
		return {d_Name, s_Name, t_Name, row_Num1, col_Num1, row_Num2, col_Num2}
	end tell # Numbers
end get_SelParams

#=====

on parleAnglais()
	local z
	try
		tell application "Numbers" to set z to localized string "Cancel"
		# Long instruction useful when iPlay '13 is installed
		#tell application "/Applications/iWork '09/Numbers.app" to set z to localized string "Cancel"
	on error
		set z to "Cancel"
	end try
	return (z is not "Annuler")
end parleAnglais

#=====

on decoupe(t, d)
	local oTIDs, l
	set oTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to d
	set l to text items of t
	set AppleScript's text item delimiters to oTIDs
	return l
end decoupe

#=====
(*
- properties which may be defined directly :
birth date (date) : The birth date of this person.
company (boolean) : Is the current record a company or a person.
department (Unicode text) : Department that this person works for.
first name (Unicode text) : The First name of this person.
home page (Unicode text) : The home page of this person.
image (TIFF picture) : Image for person.
job title (Unicode text) : The job title of this person.
last name (Unicode text) : The Last name of this person.
maiden name (Unicode text) : The Maiden name of this person.
middle name (Unicode text) : The Middle name of this person.
nickname (Unicode text) : The Nickname of this person.
note (Unicode text) : Notes for this person.
organization (Unicode text) : Organization that employs this person.
phonetic first name (Unicode text) : The phonetic version of the First name of this person.
phonetic last name (Unicode text) : The phonetic version of the Last name of this person.
phonetic middle name (Unicode text) : The Phonetic version of the Middle name of this person.
suffix (Unicode text) : The Suffix of this person.
title (Unicode text) : The title of this person..
*)

(*
- properties which must be defined indirectly :
components of address
email
URL
Group
*)

#=====

on add_update_record(the_LastName, work_flag, the_Street, the_Zip, the_City, the_MailAddress, the_Url, the_FirstName, group_Name)
	local the_Person, oldAddress, needWork
	
	tell application "Contacts"
	# using terms from application "Contacts"
		(* try to grab an existing record *)
		try (* I moved it to comply with Leopard Behavior *)
			set the_Person to (get first person whose (first name is the_FirstName) and (last name is the_LastName))
			#try
			the_Person (* Generates an error if there is no such a person *)
			(*
We are here if there is already a record for such first and last name. Edit it.
*)
			tell the_Person
				if the_MailAddress is not missing value then
					try (* 1 *)
						set value of (get email 1) to the_MailAddress
					on error
						(* We are here if there was no email data in the record, create it *)
						try (* 2 *)
							if work_flag then
								make new email at the end of emails with properties {value:the_MailAddress, label:"work"}
							else
								make new email at the end of emails with properties {value:the_MailAddress}
							end if
						end try (* 2 *)
					end try (* 1 *)
				end if # the_MailAddress
				
				if the_Url is not missing value then
					try (* 1 *)
						set value of (get url 1) to the_Url
					on error
						(* We are here if there was no URL data in the record, create it *)
						try (* 2 *)
							if work_flag then
								make new url at the end of urls with properties {value:the_Url, label:"work"}
							else
								make new url at the end of urls with properties {value:the_Url}
							end if
						end try (* 2 *)
					end try (* 1 *)
				end if # the_Url
				
				set oldAddress to (get address 1) (* CAUTION ! doesn't return an error if we never create an address *)
				if (get properties of oldAddress) is {city:missing value, formatted address:missing value, zip:missing value, label:"other", street:missing value, id:missing value, state:missing value, class:address, country code:missing value, country:missing value} then make new address at the end of addresses
				
				tell oldAddress
					if the_Street is not missing value then set street to the_Street
					if the_Zip is not missing value then set zip to the_Zip
					if the_City is not missing value then set city to the_City
					if work_flag then set label to "work"
				end tell # oldAddress
				
			end tell # the_Person
			
		on error
			(* 
We are here if there was no such a record. Create a new one.
*)
			try (* 1 *)
				if the_FirstName is missing value then
					set the_Person to (make new person with properties {last name:the_LastName, company:work_flag})
				else
					set the_Person to (make new person with properties {first name:the_FirstName, last name:the_LastName, company:work_flag})
				end if
			end try (* 1 *)
			tell the_Person
				if work_flag then
					try (* 1 *)
						make new email at the end of emails with properties {value:the_MailAddress, label:"work"}
					end try (* 1 *)
					try (* 2 *)
						make new address at the end of addresses with properties {street:the_Street, zip:the_Zip, city:the_City, label:"work"}
					end try (* 2 *)
				else
					try (* 1 *)
						make new email at the end of emails with properties {value:the_MailAddress}
					end try (* 1 *)
					try (* 2 *)
						make new address at the end of addresses with properties {street:the_Street, zip:the_Zip, city:the_City}
					end try (* 2 *)
				end if # work_flag
				try (* 3 *)
					make new url at end of urls with properties {value:the_Url, label:"url"}
				end try (* 3 *)
			end tell # the_Person
		end try
		(*
We are here in both cases
		*)
		if group_Name is not missing value then
			if group_Name is not in (get name of every group) then (* check if there is already such a group *)
				make new group with properties {name:group_Name} (* create a new group *)
				repeat until exists group group_Name (* wait to be sure that the group is created *)
					delay 0.2
				end repeat
			end if # group_Name is not in .
			
			add the_Person to group group_Name (*  inserts the person in the group.
No provision to move a record out of the group *)
		end if # group_Name is not missing value
		
		save
		# end using terms from
	end tell # AddressBook
end add_update_record

#=====

#[/SCRIPT]
#{code}

Yvan KOENIG (VALLAURIS, France) lundi 23 décembre 2013 18:13:15

Merry X-mas.

I am sorry it seems that my related names field called “region” is misleading.

Actually it is used for sales region which is completely different than the region of the address fields of the card.

I finally figured out how to collect separately the related fields.

It seems that the fields are numbered in Contacts and only their values are saved as what is entered inside.

i.e. if you create a custom field within related names area it will be recorded as first(if this is the first one and as 20th if this is the twentieth one and so on…)

Here is a script example. (I use 34th because this is my last related names field.)


set myRelatedNames to {}
					if (count of related name) > 0 then
						set descripteurs to 34th related name
						if class of descripteurs is not list then
							copy ("" & label of descripteurs & ", " & value of descripteurs) to end of myRelatedNames
						else
							repeat with unDescripteur in descripteurs
								copy ("" & label of unDescripteur & ", " & value of unDescripteur) to end of myRelatedNames
							end repeat
						end if -- class of.
					end if
					
					set myRelatedNames to my recolle(myRelatedNames, ", ")