views:

98

answers:

1

I am trying to clean the holes out of my Mac address book. As a first step I want to ask all my friends for their birthday, to be able to congratulate them with cheesy Hallmark cards.

I need a "group" in my address book, to mailmerge personalized messages from.

This is the Applescript I came up with:

tell application "Address Book"

make new group with properties {name:"No Birthday"}

set birthdayPeople to (get every person whose birth date is greater than date "Monday, January 1, 1900 12:00:00 AM")

repeat with i from 1 to number of items in people

    set thePerson to item i of people

    if not (birthdayPeople contains thePerson) then
        add thePerson to group "No Birthday"
    end if

end repeat
save 
end tell

It breaks, but from the error messages I cannot deduce what is wrong:

Result: error "Can’t make «class azf4» id \"05F770BA-7492-436B-9B58-E24F494702F8:ABPerson\" of application \"Address Book\" into type vector." number -1700 from «class azf4» id "05F770BA-7492-436B-9B58-E24F494702F8:ABPerson" to vector

(BTW: Did I mention this is my first AppleScript code, EVER? So, if this code can be simplified, or made more elegant, that is welcome too.)

+3  A: 

We can simplify this down to five lines:

tell application "Address Book"
    add (every person whose birth date is missing value) to ¬
        make new group with properties {name:"No Birthday"}
    save addressbook
end tell

It turns out that add can take a list as well as a single object, so we can skip the loop. (Note that if you had used a loop, you could have used the repeat with thePerson in people ... end repeat form, as that's cleaner than what you had.) Next, it appears that Address Book stores missing birthdays as missing value (effectively null), so we should check for that rather than comparing against an early date. Thus, to get a list of the birthdayless people, we write every person whose birth date is missing value, which does exactly what it says. The whose clause filters the preceding list so that it only contains values matching the given predicate. We then add that list to the new group, which we create inline. Finally, save addressbook saves the changes and makes them visible.

However, in this case, you don't need an AppleScript at all. Address Book supports smart groups; to create one, option-click the new group button. Choose "No Birthdays" for the smart group's name, and tell it to match cards for which "Birthday" (from the first dropdown) "is not set" (from the second dropdown). This will give you a dynamically-updating group of people who have no set birthday.


Edit: It seems that while add takes a list on Leopard, it doesn't on Snow Leopard, so we'll need to use an explicit repeat loop (which works on Leopard too):

tell application "Address Book"
    set noBirthdays to make new group with properties {name:"No Birthday"}

    repeat with p in (every person whose birth date is missing value)
        add p to noBirthdays
    end repeat

    save addressbook
end tell

This works almost the same way as the above solution, except we make the group beforehand, and then add each item individually. Rather than iterating over the indices, we iterate over the elements directly, since that's all we care about here.


Edit: For other properties, things are similar; in fact, when using the smart group, things are identical. From the AppleScript side, the question is the format of the data. Most slots have missing value when they're unset, but email is an exception. As we can see by testing, they're stored in a list, so we want every person whose email is {}. However, this doesn't work for me—probably because email is a list, but I'm not sure—so you'll instead want

    repeat with p in every person
        if email of p is {} then add p to noEmails
    end repeat

And voilà, everything should work.

In- and excluding businesses is also easy, but as far as I can tell, Address Book doesn't provide a way to create a smart group for them. However, the relevant property is company, which is true for businesses and false for others. Thus, to create a list without companies, you want to do

    repeat with p in (every person whose company is false)
        add p to noCompanies
    end repeat

If you want one master list with all of these criteria, there are two ways. First, from AppleScript, you take the two condition and and them, then add conditioning on whether or not they have an email:

    repeat with p in (every person whose company is false and ¬
                                         birth date is missing value)
        if email of p is {} then add p to masterGroup
    end repeat

The other option is to do this from Address Book. You first need to create a No Companies group the AppleScript way. You then create a smart group matching all of three conditions (you add them with the + button): "Email is not set", "Birthday is not set", and "Card is a member of 'No Companies'". This is probably the best option, although it's unfortunate that you have to use AppleScript for the No Companies group (I feel like there should be a way, but I can't see what it is).

Antal S-Z
I ran it, but: error "Address Book got an error: every person whose birth date = missing value doesn’t understand the add message." number -1708 from every person whose birth date = missing value
Felix Ogg
That's odd, it works for me. What version of Mac OS X are you running? You could switch to a `repeat with p in every person whose ...` loop. And is there a reason you can't use the smart group? That seems like a better option.
Antal S-Z
I forgot to tell that the smart group worked wonders, thanks for that tip. However, the AppleScript solution did not, and I am still interested in it; I want to extend it with some more code. I am on the latest version of Mac OS X, snow leopard.
Felix Ogg
Glad to hear that the smart group worked. As for the code, I'm running Leopard (10.5), so I guess there's been a change; I've edited the question to include something that should work.
Antal S-Z
I will accept your answer, it works. If you can find the time, I'd appreciate a hint on filtering out the businesses and people without e-mail address.
Felix Ogg
I've updated my answer to include that. I hope it helps!
Antal S-Z