Writing AppleScripts

The two tasks required before writing AppleScripts are as follows. First, you need to know how to use the AppleScript programming language. This documentation will not explain that process so you should use other references. Second, GEDitCOM II allows scripts to access all your genealogy data, but some access is done through the GEDCOM format. Thus, for some tasks, you may need to be familiar with GEDCOM data structures. GEDCOM documentation can be found in the GEDitCOM II help or on line.

AppleSript scripts are usually written using Apple's Script Editor (although other applications are available). To edit any script in the Extensions menu, select the script while holding down the option key. The script will open in your preferred script editor (which is Apple's Script Editor by default, but can be changed to any other editor using standard MacOS methods).

The AppleScript scripting topics are:

Hello GEDitCOM II

When learning a new programming tool, the first step is often a trivial program to see something happen. Here is a trivial AppleScript for GEDitCOM II:

tell application "GEDitCOM II"
   if number of documents is 0 then
      say "No documents are open."
      return
   end if
   tell front document
      set selrecs to selected records
      if (count of selrecs) is 0 then
         say "No record is selected."
         return
      end if
      tell item 1 of selrecs
         say (description output options {"SAY"})
      end tell
   end tell
end tell

To try this script do the follow:

The key features in this (or any AppleScript) are the tell blocks (e.g., tell application "GEDitCOM II" and tell front document). Within these blocks, you enter the scripting world of the application, a document, or other objects where you can access properties of the objects or send commands to the objects. In GEDitCOM II, those other objects are GEDCOM records, GEDCOM structures, and albums. The remainder of this page gives information are working with all of GEDitCOM II's objects.

The second thing you can do with any script is to install it into GEDitCOM II. The steps are easy:

Your script will now appear in the scripts menu. You can select it from that menu to run it.

Although the above steps work, since GEDitCOM II, version 2.0, it is much better to package scripts into a GEDitCOM II "Plug In" or "Extension". During initial development it is easiest to work on the script alone, but at some point it should be moved to an extension.

Writing GEDitCOM II AppleScripts

The process of becoming an expert GEDitCOM II script writer will include some of the following steps:

Working with Genealogy Documents

You can create and access genealogy documents opened in GEDitCOM II using the standard document class in AppleScripts:

tell application "GEDitCOM II"
   make new document at front document
   tell front document
      (access document's data)
   end tell
end tell

The make command creates a new document and you can add data using options described in the following sections. Most GEDitCOM II scripts will work on the data in the current front document and thus most scripts will have a tell front document block. All GEDitCOM II scripts will relate to GEDitCOM II and thus much of the script will be enclosed in tell application "GEDitCOM II" blocks; that block is implied for all the examples given below.

GEDitCOM II documents have several properties and most are adequately defined in GEDitCOM II's dictionary. The following, however, require additional documentation:

key record
This property returns a single record relevant for the front most window in the document. For most windows, it is simply a selected record, but for family tree windows, it is the root record in the tree (even if a different record is selected).
listed records
This property returns of list of records for the front most window in the document. Its meaning depends on the type of window:

Working with Genealogy Records

Accessing Records

You can read data from any GEDCOM record by accessing the record by number, by name, or by id:

id of gedcomRecord 6 of document 1
record type of gedcomRecord id "@I23@" of document 2
gedcom of gedcomRecord named "Smith, John" of document 1

The record number is the order of the records in the file. The id of a record is its GEDCOM Record ID. The name of a record is the name as it appears in the index window for a column displayed using the view expression.

Because of the way data is stored in GEDitCOM II and the way AppleScript accesses the data, it is preferable to avoid accessing records using the gedcomRecords of documents. It is preferable to access data directly by record type:

name of individual 6 of document 1
husband of family id "@F1@" of document 2
referenced by of source 2 of document 1

See the GEDitCOM II dictionary for the names of each type of GEDCOM Record.

Creating Records

You can create new records with the make command:

make new individual with properties {name:"John Smith"}

You can specify any property listed is the GEDitCOM II dictionary as long as the property is not listed as read only ("r/o"). If you set the id in properties, it must be a unique record ID for the file, otherwise it will cause an error. Furthermore, if you want to set the id, it must be set in properties when you create the record; it cannot be changed after the record is created. Warning: many properties included in the make command (but not the id) are not set immediately. If you try to access the properties in the new record, they may not be available. For example, the script:

tell front document
   set newRec to make new family with properties {husband:individual id "@I350@"}
   set hRef to husband of newRec
end tell

will fail to find the husband because it will not be attached until later. If this situation causes a problem, the solution is to create the record first and then set the properties with additional commands to the new record. The following script works as expected:

tell front document
   set newRec to make new family
   set husband of newRec to individual id "@I350@"
   set hRef to husband of newRec
end tell

You can create custom record types by setting the record type in properties to a custom GEDCOM record type and making a new record of type gedcomRecord. According to the GEDCOM standard, custom record types must begin with an underscore character (_). If the custom type does not begin with an underscore, it will cause an error. A sample script for a custom record is:

tell front document
   make new gedcomRecord with properties {record type:"_PLACE"}
end tell

The make command returns a reference to the newly created record. Although AppleScript has commands to create objects at specific locations (e.g., after individual 3), those references are ignored in GEDitCOM II. The new record is always inserted into the current records using the current sort order for the type of record created. Similarly, commands to replace another record (e.g., at individual 4) are changed to creating the new record, but not deleting the other one.

Moving Records

The move command (and its synonyms copy or duplicate) has a different meaning in GEDitCOM II. You can move any record to an album (see below) and it will add that record to the album. You can also move many types of records to other records and it will mimic the drag and drop features of GEDitCOM II. In other words, moving a record is the same as dropping a record on the record browser of another record. Some examples are:

move individual id "@I23@" to family id "@F12@"
move source 3 to individual id "@I24@"
move multimedia named "John Smith Portrait" to individual named "Smith, John"

See the editing help in GEDitCOM II for each type of record to know which moves (analogous to drops) are supported. Some common moves are

Deleting Records

You can delete a record by number, by name, or by id:

delete individual named "Smith, John" of document 1
delete family 4 of document 2
delete multimedia id "@M12@" of document 1

Locating Records

The fastest way to locate records with certain properties is to make use of AppleScript's every command. For example, to find all individuals born in the 1920's, you could use

tell front document
   set {b1, b2} to sdn range full date "FROM 1920 TO 1929"
   set indis to every individual whose birth SDN is greater than b1 and ¬
           birth SDN max is less than b2
end tell

You can locate records of any type by using any combination of conditionals based on the available properties for that type of record. You should try to find records by this method when possible, because it is much faster then looping over records looking for matches.

Working with GEDCOM Data in Records

There are three ways to read or change the data in GEDCOM records:

  1. Directly read or set properties for a record
  2. Read or edit any GEDCOM structure
  3. Read or edit the entire raw GEDCOM data
  4. Honoring record restriction

Directly read or set properties for a record

The different types of GEDCOM records have properties that can be "read" and "set." Any property listed in the GEDitCOM II dictionary can be read; those not specified as read only ("r/o") can also be changed. For example:

tell front document
   set bd to birth date of individual 1          -- read the birth date
   set name of individual 3 to "Henry James"     -- change the name
end tell

Although only some properties can be set using the above method, all other data can be edited using methods in the following sections.

Most properties for each type of record and adequately defined in GEDitCOM II's dictionary. The following, however, require additional documentation:

contextInfo
This property is provided for use by scripts allowing them to store any information on a record and retrieve it later during the script. Any AppleScripting object can be stored on the record. A script should always clear the contextInfo before using it. For example, to use contextInfo of individuals, the script should start by clearing them with
set contextInfo of every individual to ""
Note that it is much faster to set the value for every individual in one command then to loop over individuals and set their contextInfo one at a time. You can also read one property for all records in a single command:
set currentInfo to contextInfo of every individual

Read or edit any GEDCOM structures in a record or in another structure

GEDCOM records are organized into GEDCOM structures. You can read any structure that is not hard-wired to a property by using the evaluate command such as

set cd to evaluate expression "CENS.DATE" of individual 6

This command will read the date for the first census event in the current individual by using a GEDCOM expression. For more details on this command, see the evaluate command.

You can directly access GEDCOM structures by number or name (where name refers to the GEDCOM tag word for the structure) and then work with the properties of that structure. For example, to access and change the NCHI structure (or number of children, if it exists) of a family record, you can:

set nchi to a reference to structure named "NCHI" of family id "@F23@"
set contents of nchi to "3"

The second line means you can set the contents of any structure, where "contents" is the line value for the first line of the structure. You can also change the GEDCOM tag (using the name property), but you cannot change the level number. If there is more than one structure with the same name, accessing it by name method will return the first one. The best way to access multiple structures, or to access structures without using name or number, is to use the find structures command.

GEDCOM structures themselves are organized into subordinate structures. You can access any substructure by number or name within the parent structure. For example, the divorce date of an family might be at

set div to a reference to structure named "DIV" of family 23
tell div
   set dd to a reference to structure named "DATE"
   set contents of dd to "12 JUL 1845"
end tell

The above methods assume the structure you want to access already exists. If it does not exist, you may want to create it. You can create new structures and insert them at any location or even have them replace an existing structure:

make new structure with properties {name:"DATE",contents:"4 JUL 1776"}
make new structure at after structure 3 with properties {name:"BIRT"}
make new structure after structure 3 with properties {name:"BIRT"}

You should always specify the name, or GEDCOM tag, for the structure in its properties when making a new structure. The first make puts the structure at the end of the current record or structure. The second one inserts it after the previous third structure. The last one replaces (so be careful) structure 3 with the new structure. The make command returns a reference to the new structure, which you can use to change it or to add subordinate structures.

You can rearrange structures using the move, copy, or duplicate commands.

move structure named "SPFX" to after last structure of structure named "NAME"
move structure named "SOUR" to after last structure of note id "@N34@

You can move structures from one location to another including moves to then be subordinate to a different structure or even to be in a different record. The move options will automatically make sure the GEDCOM line level number is adjusted, if needed, for the new location.

You can delete structures with the delete command.

Warning: when you move, add, or delete structures, you may invalidate previously defined references to structures in the same record. For example, if you have a reference to structure 5, but then delete structure 4, that reference will still reference structure 5, but structure 5 will now be a new structure (the previous structure 6). In brief, whenever adding or deleting structures, be careful of previously defined references that may now point to different data.

Edit the entire GEDCOM data for a record or structure

You can read and set the entire GEDCOM data for a record. Reading the GEDCOM data is safe and has many uses. Changing the GEDCOM data, however, might corrupt the data in your file and/or break existing linkages between records. It should only be used by those very familiar with the GEDCOM format and usually only for special applications.

set recText to gedcom of family 2 of document 1
   (... use AppleScript text commands to change recText)
set gedcom of family 2 of document 1 to recText

Note this method can only set GEDCOM of GEDCOM records and not of GEDCOM structures. The first line of the new GEDCOM data must begin in "0" (to indicate it is a record), but the ID and record type in that first line will be ignored and replaced by values in the original record. In other words, this approach only changes subordoinate data within the record.

Honoring record restriction

A user can restrict editing of record in GEDitCOM II using a GEDCOM RESN tag. Such a restriction can prevent editing of the record in GEDitCOM II, but does not block scripts in extensions from changing the data. A robust extension should either check for restrictions before changing any data, or document in extension help that it might change locked records.

AppleScripts can check for restrictions using:

tell front document
  if restriction of individual 1 is not locked then
    -- it is "unlocked", "privacy", or "confidential"
  else
    -- it is locked so do not change it
end tell

Note that only individual or family records have restriction property and can be locked. Getting restriction property of some other record type will cause an AppleScript error.

Working with Dates

A common need in scripts is to work with dates in your file. The challenge in genealogy is that dates are often incomplete. For example you might know you great-grandfather was born in 1872 but not know the month or day. Similarly a date might be a month and a year (MAR 1842) or even a range of dates (BET 1900 and 1911). These "fuzzy" dates are confusing to many programming languages. The built-in date features of AppleScript (like date features of most programming languages) expects all dates to be an exact date like "12 JUN 1949" and therefore does not know how to handle genealogy dates. Fortunately, GEDitCOM II scripting provides tools for working with "fuzzy" dates.

The basic task for all date calculations is to convert the date to a serial day number (SDN) which assigns an increasing number to all days with day 1 being a date around 4000 BC. When the date is a "fuzzy" date, the single SDN is represented by a range in day numbers spanning the earliest possible day represented by that date to the last possible day. For example the "fuzzy" date 1872 would correspond to the SDN range {2404794, 2405158} where 240474 is the SDN of 1 JAN 1872 and 2405158 is the SDN for 31 DEC 1872.

In date scripts, the first task is fetch the SDNs or SDN ranges for dates in your file. Many of the most common dates are available as properties of records. Some examples are:

set bdmin to birth SDN of individual 6
set bdmax to birth SDN max of individual 6
set ddrange to {death SDN of individual 3,death SDN max of individual 3}
tell family 4
   set mrange to (marriage SDN,marriage SDN max}
end tell
tell structure named "CENS" of individual 10
   set cd to event SDN
end tell

The SDN property gets the minimum SDN for the given date; the SDN max property gets the maximum SDN for the given date. If the date is not available, the returned SDN is zero.

For dates not connected to a property or for dates just in a text string, you can use the sdn range function. You can combine two GEDitCOM II commands to get today's SDN:

sdn range full date today

which on 1 JAN 2010 returns {2455198, 2455198} (note that the SDN range for an exact date has the same minimum and maximum).

Once you have SDN's for dates, calculations are simple and can use standard AppleScript methods for working with numbers. For example:

tell individual 2
   set ageAtDeath to (death SDN - birthSDN)/365.25       -- age based on minimum SDNs
   set minAge to (death SDN - birth SDN max)/365.25      -- minimum possible age
   set maxAge to (death SDN max - birth SDN)/365.25      -- maximum possible age
end tell

To convert back to a date (using the user-preferred date style), you can use the date text command:

date text sdn 2455032       -- gives 19 Jul 2009

Lastly, for more advanced calculations, the Date Calculator application (which is provided with GEDitCOM II in the "GEDitCOM Extras" folder in your "Applications" folder) is also scriptable and can do more date calculations. Here is a sample conversion of a date to the Jewish calendar:

tell application "GEDitCOM II"
   tell front document
      set bd to birth date of individual named "Spencer, Arthur Charles"
   end tell
end tell

tell application "Date Calculator"
   set jewishDate to (convert date bd to "Jewish")
end tell

Refer to the Date Calculator help information for its options for scripting of date calculations.

Working with Albums

You can access existing albums in the document and then access records in that album or add new records to the album. A common task in scripts is to make a new album and then fill it with records:

tell document 1
   set newAlbum to (make new album with properties {name:"My Album"})
   move individual 6 to newAlbum
end tell

You can delete albums, but cannot move, copy, or duplicate them.

Since albums can have more than one type of record, you have to access records by the generic record class or gedcomRecord:

gedcomRecord 3 of album 1
delete gedcomRecord 4 of album named "New Album"

Here the delete command will remove the record from the album, but not delete it from the file. But, be careful - the similar command delete gedcomRecord 1 given at this same place but not mentioning an album will delete the record from the file.

Writing Customizable Reports

GEDitCOM II has numerous built-in reports accessible through the reports menu. But you are not limited to those reports. By writing a custom script, you can define new reports and display them in a record browser the same way built-in reports are displayed. The following outline of a script creates a custom report:

tell application "GEDitCOM II"
  set rpt to {"<div>" & return}
  set end of rpt to "<h1>My Custom Report</h1>" & return
     ...
     (insert any text formatted using html methods)
     ...
  set end rpt "</div>"
  set theReport to rpt as string

  tell front document
    set newreport to make new report with properties ¬
          {name:"My Custom Report", body:theReport}
    show browser of newreport
  end tell
end tell

The first section reads data in the file and combines anything you want into a list variable (here called rpt, hint: building the report in a list and then converting to a string at the end is much faster than building the report by concatenating strings using commands like set rpt to rpt & "more text"). The formatting of a report for nice style is done using html commands. The report must start end end with <div> and </div> elements, respectively, to tell GEDitCOM II html content is being used. The report will use the same report.css cascading style sheet (stored at ~/Library/Application Support/GEDitCOM II/System/Formats/reports.css) that built-in reports use. Thus, if you use standard html command, your custom report will look similar to GEDitCOM II built-in reports.

One nice feature of html content is providing links to other web sites. An additional option in GEDitCOM II is that your report can provide links to open any record in the file. Links to records use this html style:

<a href='//@I128@'>Smith, John</a>

The link is a standard html "<a href=" link, but the reference must start with two slashes "//" (a requirement unexpectedly added by Apple MacOS that used to work without the slashes). The link is any record ID (which can be read with @rec.id where rec is a record's object name. The text inside the link is the clickable text that will appear in the report. Besides links to records or outside web sites, you can add internal links within the report by standard html methods. Such links are helpful in long reports.

You are not limited to loading reports into GEDitCOM II and then displaying in a record browser. You may prefer to send your report to any other scriptable Macintosh software (e.g., Pages or MS Word). When using this method, your script will use scripting options in the target software to store the report and format as desired. If the report is sent to word processing software, you can then directly open and edit more if desired. To write such a report, refer to the dictionary for the target application for options you have for loading the report text and setting styles for the report text. Note that scripts that target outside applications will only work on computers that have that application. In other words, if you write a script that sends a report to MS Word, it can only be used by people that have MS Word, even if they do not plan to use the MS Word options in the script.

Some of the extensions provided with GEDitCOM II create custom reports. You can refer to the documentation and scripts in those extensions more examples of creating reports. There are also posted tutorials about creating custom reports including utility subroutines for working with MS Word or Pages.

Returning the Result of a Script

While a script is running, you can display text messages in the scripting panel by using the notify progress command with a message parameter. These messages are usually meant to display information about the progress of a long script.

When a script ends, it can use the return command to return nothing or to return a string that describes some results of running the script:

return "This script changed 5 records"

If nothing is returned, the script panel in GEDitCOM II will be immediately dismissed (even if notify progress was used to write progress messages). If the the script returns a string, however, the script panel will remain option. The panel and the returned string will stay visible until closed by the user or until another script is run. The panel can be resized and the user can select and copy text.

AppleScripting Tutorials

After reading the above information on writing AppleScripts and browsing all the available Scripting commands, the best way to proceed is to see some sample scripts. To see sample scripts, you can browse the on-line Scripting tutorials on geditcom.com. The tutorials also include utility subroutines and methods that you can copy and paste into your own scripts.

Performance of AppleScript

AppleScript is a powerful, flexible, and abstract language, which allows you to enhance GEDitCOM II in many ways. You will find, however, that scripting features of GEDitCOM II will be slower then built-in features. Scripts are ideal for changes to a few records (e.g., the currently selected records) or even for a global change you expect to only make a few times (e.g., changing the case on all names once). Although global changes may be slow, but will always be much faster and more reliable than doing all the changes by hand.

Scripts are less ideal for frequent global transformations of all the data in a file. The preferred approach for such complicated transformations is to use built-in features of GEDitCOM II. But, if you need complicated changes not already built-in to GEDitCOM II, a script can serve as a bridge until that feature is available. In fact, a script can be the prototype for a feature to be built into a future version of GEDitCOM II.

Finally, the speed of scripts will depend on how they are written. The two most important techniques for maximum effiency in scripts are:

  1. When accessing all records, try to use a bulk method that accesses them all at once rather then accessing them individually in a loop (e.g., see method for locating records and method for setting contextInfo).
  2. When building a long string, it is better to put all pieces of the string into a list and they join the list into a string at the end (e.g., see outline for building a report).