Tuturial Index → Apple Scripting
The AppleScript for this report in the "CreateReportTutorial.gplug" extension. The main script is in the "Generation Ages To Report.applescript". To run this tutorial report, select "Custom Report Tutorial" from the Extensions menu and then choose the "AppleScript Custom Report" option. To view the script source, expand "Properties" section of the extension, click on "other files" and then double click on "Generation Ages To Report.applescript". The AppleScript will open in Script Editor for viewing and editing.
-- Key properties and variables property scriptName : "Generational Ages" global numHusbAge, numWifeAge, numFathAge, numMothAge global sumHusbAge, sumWifeAge, sumFathAge, sumMothAge -- Verify OK to run this script if CheckAvailable(scriptName,2.0) is false then return -- Choose all or currently selected family records tell application "GEDitCOM II" set whichOnes to user option title  "Get report for All or just Selected family records"  buttons {"All", "Cancel", "Selected"} if whichOnes is "Cancel" then return -- Get a list of the chosen family records if whichOnes is "All" then set fams to every family of front document else set selRecs to selected records of front document set fams to {} repeat with selRec in selRecs if record type of selRec is "FAM" then set end of fams to selRec end if end repeat end if end tell -- Exit now if no family records were chosen if (count of fams) is 0 then return "No family records were selected" end if -- Collect all report data in a subroutine CollectAges(fams) -- Write to report and then done WriteToReport()
Listing 1 shows the main report script, This listing omits comments at the start of the script you can see in the GEDitCOM Editor. All good scripts should start with comments that describe its function along with any other relevant details.
The Preamble section defines a AppleScipt property and global variables used by this script. Next, a CheckAvalable() utility method is called to verify a document is open the current version being reun is sufficient to run this script. Although GEDitCOM II does not have an AppleScripting module (as in Python), you can copy and paste any of the utility methods on geditcom.com or write your own methods and copy them to multiple AppleScripts,.
If CheckAvailable() returns false, the script cannot run and it exits. It it returns true, the next section targets the GEDitCOM II application to choose and find the records used for the report. The choosing is done with the "User Option" command, Once the choice is made, the records to be analyzed are loaded into a list in fams. The process for "selected records" is harder in AppleScript then in Internal Scripts or in Python scripts. If no family records are found, the script exits.
The remainder of the main script does the bulk of the work in two subrouties defined in the next two sections
(* Collect data for the generational ages report *)
on CollectAges(famList)
-- initialize variables
set {numHusbAge, sumHusbAge, numFathAge, sumFathAge} to {0, 0, 0, 0}
set {numWifeAge, sumWifeAge, numMothAge, sumMothAge} to {0, 0, 0, 0}
set {fractionStepSize, nextFraction} to {0.01, 0.01}
set numFams to number of items in famList
tell application "GEDitCOM II"
repeat with i from 1 to numFams
-- read family record information
tell item i of famList
set {husbRef, wifeRef, chilList} to {husband, wife, children}
set mdate to marriage SDN
end tell
-- read parent birthdates
set {hbdate, wbdate} to {0, 0}
if husbRef is not "" then
set hbdate to birth SDN of husbRef
end if
if wifeRef is not "" then
set wbdate to birth SDN of wifeRef
end if
-- spouse ages at marriage
if mdate > 0 and hbdate > 0 then
set husbAge to my GetAgeSpan(hbdate, mdate)
set {numHusbAge, sumHusbAge} to {numHusbAge+1, sumHusbAge+husbAge}
end if
if mdate > 0 and wbdate > 0 then
set wifeAge to my GetAgeSpan(wbdate, mdate)
set {numWifeAge, sumWifeAge} to {numWifeAge+1, sumWifeAge+wifeAge}
end if
-- spouse ages when children were born
if hbdate > 0 or wbdate > 0 then
repeat with chilRef in chilList
set cbdate to birth SDN of chilRef
if cbdate > 0 and hbdate > 0 then
set fathAge to my GetAgeSpan(hbdate, cbdate)
set {numFathAge, sumFathAge} to {numFathAge + 1, Â
sumFathAge + fathAge}
end if
if cbdate > 0 and wbdate > 0 then
set mothAge to my GetAgeSpan(wbdate, cbdate)
set {numMothAge, sumMothAge} to {numMothAge + 1, Â
sumMothAge + mothAge}
end if
end repeat
end if
-- time for progress
set fractionDone to i / numFams
if fractionDone > nextFraction then
notify progress fraction fractionDone
set nextFraction to nextFraction + fractionStepSize
end if
end repeat
end tell
end CollectAges
This subroutine collects all data on ages from the choosen family records in the file. It is where most of the work of this script is done; the work is done by interaction with your data through GEDitCOM II's scripting objects and their properties.
The first section defines and initializes global variables to accumulate age information. The repeat loop enumerates over all family records passed to this subroutine. The loop starts by reading data from the family record - namely references to the husband and wife records, a list of all children records, and the marriage date. The marriage date, like all dates in this script, is read as a serial day number. In AppleScript, one has to be careful about targeting of commands. The two options are to embed commands within a "tell" block. For example, the tell item i of famList is target the next family record in famList. The commands in that blick get data from that record. The other option is to include the object in the command. For example:
set hbdate to birth SDN of husbRef
gets birth serial day number of the husband record.
The next section reads the parents' birth dates (if available). If marriage date is known, the age of each spouse when married is calculated and added to global variables. The calculations use the GetAgeSpan() utility method.
The age at child birth section is similar. It contains a loop over all children in the family. For each child, it looks for their birth date. If a birth date is found, the ages of each parent with a known birth date are added to global variables. This entire section is enclosed in a conditional that says to do these calculations only if at least one parent birthdate is known.
Scripts that process many records, especially in large files, might take while to finish. It is good practice to provide feedback of script progress to the user. Here the variable fractionStepSize defines how often to notify the user and nextFraction is when the next notification occurs. The progress section at the end of the loop calculates current fracture done and when needed, notifies user of progress using the notify progress scripting command.
When this subroutine is done, the global variables (e.g., numHusbAge, sumHusbAge, etc.) will contain all data needed to output the report. The subroutine ends and returns control to the main script. The next section explains formatting of the output report.
(* Write the results not in the global variables to a
GEDitCOM II report *)
on WriteToReport()
-- build report using <html> elements beginning with <div>
set rpt to {"<div>" & return}
-- begin report with <h1> for title
set fname to name of front document of application "GEDitCOM II"
set end of rpt to "<h1>Generational Age Analysis in " & Â
fname & "</h1>" & return
-- start <table> and give it a caption
set end of rpt to "<table>" & return
set end of rpt to "<caption>" & return
set end of rpt to "Summary of spouse ages when married " Â
& "and when children were born" & return
set end of rpt to "</caption>" & return
-- column labels in the <thead> section
set end of rpt to "<thead><tr>" & return
set end of rpt to "<th>Age Item</th><th>Husband</th><th>Wife</th>" & return
set end of rpt to "</tr></thead>" & return
-- the rows are in the <tbody> element
set end of rpt to "<tbody>" & return
-- rows for ages when married and when children were borm
set end of rpt to InsertRow("Avg. Age at Marriage", numHusbAge, Â
sumHusbAge, numWifeAge, sumWifeAge)
set end of rpt to InsertRow("Avg. Age at Childbirth", numFathAge, Â
sumFathAge, numMothAge, sumMothAge)
-- end the <tbody> and <table> elements
set end of rpt to "</tbody>" & return
set end of rpt to "</table>" & return
-- end the report by ending <div> element
set end of rpt to "</div>"
-- create a report and open it in a browser window
tell front document of application "GEDitCOM II"
set newreport to make new report with properties {name:"Generation Ages", Â
body:rpt as string}
show browser of newreport
end tell
end WriteToReport
(* Insert table row with husband and wife results *)
on InsertRow(rowLabel, numHusb, sumHusb, numWife, sumWife)
set tr to "<tr><td>" & rowLabel & "</td><td align='"
if numHusb > 0 then
set tr to tr & "right'>" & RoundNum(sumHusb / numHusb, 2)
else
set tr to tr & "center'>-"
end if
set tr to tr & "</td><td align='"
if numWife > 0 then
set tr to tr & "right'>" & RoundNum(sumWife / numWife, 2)
else
set tr to tr & "center'>-"
end if
set tr to tr & "</td></tr>" & return
return tr
end InsertRow
A good way to format reports in sripts is to use html elements enclosed within a div element. This subroutine builds the report in the rpt list that begins with a single element and the starting div element and then appends a second time for the report header. This subroutine then mainly use adds items to the rpt list. The list is converted to a string at the end.
This report creates a small table using standard html elements for a table. The table begins with a caption and a header row. The InsertRow() subroutine is called twice to insert to rows for age at marriage and age at child birth. When inserting numbers, RoundNum() utility method is used to round the results to two digits.
When the subroutine is done, a new report object is created in the target document and the body of that report is set to the rpt list cast as a string. The show browser command is used to open the report in a GEDitCOM II window. To see the results of this subroutine, run the "AppleScript Custom Report" option of this extension. When the report is done, use the "View HTML Source" menu command to see the html content.