Main Help → Writing Extensions → 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:
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:
Run the Scripticon and you should hear something about the currently selected record (if you hear nothing, make sure the volume on your compter is not muted or set too low).
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:
~/Library/Application Support/GEDitCOM II/Scripts(see GEDitCOM II help about this folder)
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.
The process of becoming an expert GEDitCOM II script writer will include some of the following steps:
- Run Apple's tool for writing scripts - the AppleScript Editor application (available in all MacOS X installations, but in different locations and by different names in various systems - it is always somewhere with the "Applications" folder).
- To see what is available for scripting of GEDitCOM II, choose the AppleScript Editor's "Open Dictionary..." menu command and select GEDitCOM II from the list. AppleScript programming often involves reading and understanding such dictionaries. You can browse GEDitCOM II's dictionary to see what objects can be scripted and for each object, see the the properties you can read and set.
- Read the information below on working with genealogy documents in scripts.
- Read the information below on working with GEDCOM genealogy records in scripts.
- Read the information below on working with GEDCOM structures within records.
- Read the information below on working with albums.
- Read about the custom AppleScript commands incorporated into GEDitCOM II.
- Read about returning a result when the script is done.
- To create custom reports, read the section on writing custom reports.
- Refer to the Scripting tutorials for more examples, sample scripts, references, and utility subroutines you can use in your scripts.
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:
{level #, individual ref} where level # is the generation number (the root for the tree starts with level 0) and individual ref is a reference to the ancestor's individual record.{level #, individual ref, link #} where level # and individual ref are as defined above and link # is a cross reference number associated with that individual (starting at 1). All subsequent times, the individual will appear as {level #, list # for first time} where list # for first time refers to the item number in listed records for the first appearance of the individual.{level #, individual ref, fam ref} where level # is the generation number (the root for the tree starts with level 0), individual ref is a reference to the spouse's individual record, and fam ref is a reference to the family record with that spouse.{level #, "", fam ref} where level # and fam ref are as for known spouses.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.
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.
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
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
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.
There are three ways to read or change the data in GEDCOM records:
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 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
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.
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.
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.
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.
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.
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.
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.
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.
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: