<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://ftp.gramps-project.org/wiki/index.php?action=history&amp;feed=atom&amp;title=Addon%3AGrampyScript</id>
	<title>Addon:GrampyScript - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://ftp.gramps-project.org/wiki/index.php?action=history&amp;feed=atom&amp;title=Addon%3AGrampyScript"/>
	<link rel="alternate" type="text/html" href="https://ftp.gramps-project.org/wiki/index.php?title=Addon:GrampyScript&amp;action=history"/>
	<updated>2026-07-12T01:44:48Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.31.3</generator>
	<entry>
		<id>https://ftp.gramps-project.org/wiki/index.php?title=Addon:GrampyScript&amp;diff=131462&amp;oldid=prev</id>
		<title>Dsblank at 22:57, 6 July 2026</title>
		<link rel="alternate" type="text/html" href="https://ftp.gramps-project.org/wiki/index.php?title=Addon:GrampyScript&amp;diff=131462&amp;oldid=prev"/>
		<updated>2026-07-06T22:57:09Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;Revision as of 22:57, 6 July 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l61&quot; &gt;Line 61:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 61:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;A script is plain Python, executed in a namespace that additionally provides the names described below. The simplest possible script:&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;A script is plain Python, executed in a namespace that additionally provides the names described below. The simplest possible script:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &lt;/ins&gt;for person in people():&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;for person in people():&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &lt;/ins&gt;row(person.gramps_id, person.name.first_name, person.surname.surname, person.gender)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &lt;/del&gt;row(person.gramps_id, person.name.first_name, person.surname.surname, person.gender)&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Iterating over records ===&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Iterating over records ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l110&quot; &gt;Line 110:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 108:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;A script can &amp;lt;code&amp;gt;import&amp;lt;/code&amp;gt; a plain &amp;lt;code&amp;gt;.py&amp;lt;/code&amp;gt; module the normal way, as long as that module lives next to the script's own &amp;lt;code&amp;gt;.gram.py&amp;lt;/code&amp;gt; file, or in the bundled &amp;lt;code&amp;gt;scripts&amp;lt;/code&amp;gt; folder. This is a convenient way to share functions between several of your own scripts instead of copy-pasting them. See &amp;lt;code&amp;gt;scripts/script_helpers.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;scripts/11_import_example.gram.py&amp;lt;/code&amp;gt; for a working example:&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;A script can &amp;lt;code&amp;gt;import&amp;lt;/code&amp;gt; a plain &amp;lt;code&amp;gt;.py&amp;lt;/code&amp;gt; module the normal way, as long as that module lives next to the script's own &amp;lt;code&amp;gt;.gram.py&amp;lt;/code&amp;gt; file, or in the bundled &amp;lt;code&amp;gt;scripts&amp;lt;/code&amp;gt; folder. This is a convenient way to share functions between several of your own scripts instead of copy-pasting them. See &amp;lt;code&amp;gt;scripts/script_helpers.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;scripts/11_import_example.gram.py&amp;lt;/code&amp;gt; for a working example:&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;&amp;lt;syntaxhighlight lang=&lt;/del&gt;&amp;quot;&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;python&lt;/del&gt;&amp;quot;&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; from script_helpers import decade&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;from script_helpers import &lt;/del&gt;decade&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; columns(&amp;quot;Decade&amp;quot;, &lt;/ins&gt;&amp;quot;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;Births&lt;/ins&gt;&amp;quot;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; counts = counter()&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; for person in people():&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; birth = person.birth&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; if birth:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; year = birth.get_date_object().get_year()&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; if year:&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; counts[&lt;/ins&gt;decade&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;(year)] += 1&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; for decade_start, count in sorted(counts.items()):&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; row(&amp;quot;%ds&amp;quot; % decade_start, count)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;columns(&amp;quot;Decade&amp;quot;, &amp;quot;Births&amp;quot;)&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;counts = counter()&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;for person in people():&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;#160; &amp;#160; birth = person.birth&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;#160; &amp;#160; if birth:&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; year = birth.get_date_object().get_year()&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; if year:&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; &amp;#160; counts[decade(year)] += 1&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;for decade_start, count in sorted(counts.items()):&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;#160; &amp;#160; row(&amp;quot;%ds&amp;quot; % decade_start, count)&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;−&lt;/td&gt;&lt;td style=&quot;color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&amp;#160;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Record fields ==&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;&amp;#160;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #222; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Record fields ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Dsblank</name></author>
		
	</entry>
	<entry>
		<id>https://ftp.gramps-project.org/wiki/index.php?title=Addon:GrampyScript&amp;diff=131461&amp;oldid=prev</id>
		<title>Dsblank: Created page with &quot;{{Third-party addon}} The {{man label|Gram.py Script}} Gramplet lets you write and run small Python scripts directly against your Gramps family tree, from a code editor embedd...&quot;</title>
		<link rel="alternate" type="text/html" href="https://ftp.gramps-project.org/wiki/index.php?title=Addon:GrampyScript&amp;diff=131461&amp;oldid=prev"/>
		<updated>2026-07-06T22:31:22Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;{{Third-party addon}} The {{man label|Gram.py Script}} Gramplet lets you write and run small Python scripts directly against your Gramps family tree, from a code editor embedd...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Third-party addon}}&lt;br /&gt;
The {{man label|Gram.py Script}} Gramplet lets you write and run small Python scripts directly against your Gramps family tree, from a code editor embedded in the Dashboard. Results show up as a sortable, double-click-to-edit table, as plain text output, or as a simple bar/pie/histogram chart. It is a reimagining of the [[Addon:Isotammi_addons#SuperTool|SuperTool]] Gramplet for Gramps 6, built directly on Gramps 6's JSON-backed data dictionaries so it can iterate over an entire tree quickly.&lt;br /&gt;
&lt;br /&gt;
{{man warn|1=Scripts run with full access to your tree|2=Code you type or open in {{man label|Gram.py Script}} executes as plain Python, with the same permissions as Gramps itself. Only run scripts you wrote yourself or that come from a source you trust, especially anything using &amp;lt;code&amp;gt;begin_changes()&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;delete()&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;set_*()&amp;lt;/code&amp;gt; to edit the database.}}&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
&lt;br /&gt;
From the Dashboard use {{man menu|Add a gramplet}} and select {{man label|Gram.py Script}}. It is recommended to detach/undock the gramplet, or give it a tall spot in the Dashboard, since the editor and results area both want vertical space (the addon defaults to a height of 800 pixels).&lt;br /&gt;
&lt;br /&gt;
The Gramplet has three parts:&lt;br /&gt;
* A code editor at the top, with Python syntax highlighting (keywords in blue, the DSL functions in green, the DSL constants in red, comments in grey).&lt;br /&gt;
* An {{man button|Execute &amp;amp;lt;Alt+Enter&amp;amp;gt;}} button.&lt;br /&gt;
* A results area below, with three tabs: {{man label|Table}}, {{man label|Output}}, and {{man label|Chart}}.&lt;br /&gt;
&lt;br /&gt;
Press {{man button|Execute &amp;amp;lt;Alt+Enter&amp;amp;gt;}} (or the {{man key press|Alt|Enter}} keyboard shortcut) to run the script currently in the editor. Whichever tab has something to show is switched to automatically: the {{man label|Table}} tab if the script called &amp;lt;code&amp;gt;row()&amp;lt;/code&amp;gt;, the {{man label|Chart}} tab if it called &amp;lt;code&amp;gt;chart()&amp;lt;/code&amp;gt;, otherwise the {{man label|Output}} tab (also used for tracebacks, when a script raises an exception).&lt;br /&gt;
&lt;br /&gt;
=== Tab completion ===&lt;br /&gt;
{{man note|Tab completion (and the addon itself) requires the [https://pypi.org/project/jedi/ jedi] Python module — see [[#Prerequisites|Prerequisites]] below.}}&lt;br /&gt;
&lt;br /&gt;
Press {{man key press|Tab}} while typing in the editor to open a completion popup for the name at the cursor. It covers both Python builtins and the {{man label|Gram.py Script}} DSL described below (&amp;lt;code&amp;gt;people&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_person&amp;lt;/code&amp;gt;, and so on), including the fields and computed properties of the record wrapper once you type a dot, e.g. &amp;lt;code&amp;gt;person.&amp;lt;/code&amp;gt;. Use {{man key press|up}}/{{man key press|down}} to move the selection, {{man key press|Tab}} or {{man key press|Enter}} to accept it, and {{man key press|Esc}} or any arrow/Home/End/Page key to dismiss the popup without accepting.&lt;br /&gt;
&lt;br /&gt;
=== Table, Output, and Chart tabs ===&lt;br /&gt;
* {{man label|Table}} — one row per call to &amp;lt;code&amp;gt;row(...)&amp;lt;/code&amp;gt; in the script, with sortable columns (click a column header). Double-click any row to open the standard Gramps editor for that record (Person, Family, Event, Place, Repository, Source, Citation, Media, or Note) — handy for reviewing or fixing records a script found.&lt;br /&gt;
* {{man label|Output}} — anything printed with &amp;lt;code&amp;gt;print()&amp;lt;/code&amp;gt;, plus the Python traceback if the script raised an exception, and a line reporting the elapsed execution time.&lt;br /&gt;
* {{man label|Chart}} — the drawing made by the last &amp;lt;code&amp;gt;chart()&amp;lt;/code&amp;gt; call in the script.&lt;br /&gt;
&lt;br /&gt;
=== Script menu ===&lt;br /&gt;
* {{man menu|Script -&amp;gt; New}} — clear the editor.&lt;br /&gt;
* {{man menu|Script -&amp;gt; Open...}} — open a &amp;lt;code&amp;gt;.gram.py&amp;lt;/code&amp;gt; file. The dialog defaults to this addon's bundled &amp;lt;code&amp;gt;scripts&amp;lt;/code&amp;gt; folder (see [[#Bundled example scripts|below]]) and shows each file's title/description as a preview when highlighted.&lt;br /&gt;
* {{man menu|Script -&amp;gt; Save}} ({{man key press|CTRL|S}}) — save over the currently open file.&lt;br /&gt;
* {{man menu|Script -&amp;gt; Save as...}} — save the current editor contents to a new &amp;lt;code&amp;gt;.gram.py&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
The last file you had open is remembered and reloaded automatically the next time you open the Gramplet.&lt;br /&gt;
&lt;br /&gt;
=== Data menu ===&lt;br /&gt;
Once a script has produced a {{man label|Table}}:&lt;br /&gt;
* {{man menu|Data -&amp;gt; Save as CSV}} — export the table's rows to a CSV file, with a choice of UTF-8/ISO8859-1 encoding and comma/semicolon delimiter.&lt;br /&gt;
* {{man menu|Data -&amp;gt; Copy to clipboard}} — copy the table's rows to the clipboard as tab-separated text, ready to paste into a spreadsheet.&lt;br /&gt;
&lt;br /&gt;
=== Keyboard shortcuts (while editing) ===&lt;br /&gt;
::{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Shortcut !! Action&lt;br /&gt;
|-&lt;br /&gt;
| {{man key press|Alt|Enter}} || Execute the script&lt;br /&gt;
|-&lt;br /&gt;
| {{man key press|Tab}} || Open completion popup (or insert 4 spaces if nothing to complete)&lt;br /&gt;
|-&lt;br /&gt;
| {{man key press|CTRL|Z}} || Undo&lt;br /&gt;
|-&lt;br /&gt;
| {{man key press|CTRL|Shift|Z}} || Redo&lt;br /&gt;
|-&lt;br /&gt;
| {{man key press|CTRL|X}} / {{man key press|CTRL|V}} || Cut / Paste&lt;br /&gt;
|-&lt;br /&gt;
| {{man key press|Alt|C}} || Copy&lt;br /&gt;
|-&lt;br /&gt;
| {{man key press|CTRL|S}} || Save script&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Writing scripts ==&lt;br /&gt;
&lt;br /&gt;
A script is plain Python, executed in a namespace that additionally provides the names described below. The simplest possible script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
for person in people():&lt;br /&gt;
    row(person.gramps_id, person.name.first_name, person.surname.surname, person.gender)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Iterating over records ===&lt;br /&gt;
Each of these is a zero-argument generator yielding one wrapped record (see [[#Record fields|Record fields]] below) per item in the tree:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;people()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;families()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;events()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;places()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;notes()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;media()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sources()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;citations()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;repositories()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Three more give you a subset instead of everything:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;selected(&amp;quot;Person&amp;quot;)&amp;lt;/code&amp;gt; — only the rows currently selected (highlighted) in that category's list view. The argument is the table/category name (&amp;lt;code&amp;gt;&amp;quot;Person&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Family&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Event&amp;quot;&amp;lt;/code&amp;gt;, ...).&lt;br /&gt;
* &amp;lt;code&amp;gt;filtered(&amp;quot;Person&amp;quot;)&amp;lt;/code&amp;gt; — every row currently visible in that category's list view (i.e. after whatever filter/search is applied there).&lt;br /&gt;
* &amp;lt;code&amp;gt;custom_filter(name, namespace=&amp;quot;Person&amp;quot;)&amp;lt;/code&amp;gt; — runs one of your own named filters, created with the Filters gramplet/editor, and yields its matches. If no filter with that name exists for that namespace, a warning is printed to the {{man label|Output}} tab and nothing is yielded.&lt;br /&gt;
&lt;br /&gt;
=== The active record ===&lt;br /&gt;
These constants hold the record currently active/selected elsewhere in Gramps (e.g. the Active Person shown in the status bar), or a blank placeholder if nothing is active:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;active_person&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_family&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_event&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_place&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_note&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_media&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_source&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_citation&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_repository&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When nothing is active, these (and any missing field on a real record) evaluate to a &amp;lt;code&amp;gt;NoneData&amp;lt;/code&amp;gt; placeholder: it prints as an empty string, is falsy in an &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;, and any attribute access or call on it just returns another empty &amp;lt;code&amp;gt;NoneData&amp;lt;/code&amp;gt; — so &amp;lt;code&amp;gt;active_person.birth.place.name&amp;lt;/code&amp;gt; is always safe to write even with nothing selected.&lt;br /&gt;
&lt;br /&gt;
=== Building the results table ===&lt;br /&gt;
* &amp;lt;code&amp;gt;row(*values)&amp;lt;/code&amp;gt; — add one row to the {{man label|Table}} tab. The number of arguments determines the number of columns; column widths and sorting are automatic (numbers sort numerically).&lt;br /&gt;
* &amp;lt;code&amp;gt;columns(*names)&amp;lt;/code&amp;gt; — set the column headers shown above the table. Call it once near the top of the script, matching the number and order of the values passed to &amp;lt;code&amp;gt;row()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Charts ===&lt;br /&gt;
&amp;lt;code&amp;gt;chart(type, data, count=20, **kwargs)&amp;lt;/code&amp;gt; draws on the {{man label|Chart}} tab. &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is one of:&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;pie&amp;quot;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;bar&amp;quot;&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;dict&amp;lt;/code&amp;gt; of &amp;lt;code&amp;gt;{label: count}&amp;lt;/code&amp;gt; (or a list of &amp;lt;code&amp;gt;[label, count]&amp;lt;/code&amp;gt; pairs); only the top &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; entries by count are drawn.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;histogram&amp;quot;&amp;lt;/code&amp;gt; — &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; is a plain list of numbers, bucketed into &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; intervals. Pass &amp;lt;code&amp;gt;decimal_places=N&amp;lt;/code&amp;gt; as a keyword argument to control how the bucket-range labels are formatted.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;counter()&amp;lt;/code&amp;gt; returns a fresh &amp;lt;code&amp;gt;collections.defaultdict(int)&amp;lt;/code&amp;gt; — a convenient accumulator for building the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; a chart or CSV report needs, e.g. &amp;lt;code&amp;gt;counts[person.gender] += 1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Editing and deleting data ===&lt;br /&gt;
{{man warn|Always undoable, never silent|Edits made by a script go through Gramps' normal undo history — {{man menu|Edit -&amp;gt; Undo}} reverts them like any other change — but they are '''not''' asked for confirmation first. Test on a backup copy of your tree first if you are not sure what a script will do.}}&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;begin_changes(message=&amp;quot;Gram.py Script Edited Data&amp;quot;)&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end_changes()&amp;lt;/code&amp;gt; — wrap a batch of edits in a single undoable transaction, so {{man menu|Edit -&amp;gt; Undo}} reverts the whole batch in one step instead of one step per record. If a script errors out or you forget &amp;lt;code&amp;gt;end_changes()&amp;lt;/code&amp;gt;, the Gramplet closes the transaction automatically once the script finishes.&lt;br /&gt;
* Setting an attribute on a record, e.g. &amp;lt;code&amp;gt;person.private = True&amp;lt;/code&amp;gt;, or calling one of its &amp;lt;code&amp;gt;set_*()&amp;lt;/code&amp;gt; methods, e.g. &amp;lt;code&amp;gt;person.set_privacy(True)&amp;lt;/code&amp;gt;, writes the change straight to the database (inside whatever transaction is open).&lt;br /&gt;
* &amp;lt;code&amp;gt;delete(record)&amp;lt;/code&amp;gt; — permanently remove a record (e.g. &amp;lt;code&amp;gt;delete(repository)&amp;lt;/code&amp;gt;). There is no separate confirmation step, so check &amp;lt;code&amp;gt;record.back_references&amp;lt;/code&amp;gt; is empty first if you only want to remove records nothing else refers to.&lt;br /&gt;
&lt;br /&gt;
=== Other helpers ===&lt;br /&gt;
* &amp;lt;code&amp;gt;today&amp;lt;/code&amp;gt; — a Gramps &amp;lt;code&amp;gt;Date&amp;lt;/code&amp;gt; object for today's date.&lt;br /&gt;
* &amp;lt;code&amp;gt;print(...)&amp;lt;/code&amp;gt; — writes to the {{man label|Output}} tab instead of a terminal.&lt;br /&gt;
* &amp;lt;code&amp;gt;database&amp;lt;/code&amp;gt; — the raw Gramps database object (&amp;lt;code&amp;gt;self.dbstate.db&amp;lt;/code&amp;gt;), for anything not covered by the helpers above.&lt;br /&gt;
&lt;br /&gt;
=== Importing your own helper code ===&lt;br /&gt;
A script can &amp;lt;code&amp;gt;import&amp;lt;/code&amp;gt; a plain &amp;lt;code&amp;gt;.py&amp;lt;/code&amp;gt; module the normal way, as long as that module lives next to the script's own &amp;lt;code&amp;gt;.gram.py&amp;lt;/code&amp;gt; file, or in the bundled &amp;lt;code&amp;gt;scripts&amp;lt;/code&amp;gt; folder. This is a convenient way to share functions between several of your own scripts instead of copy-pasting them. See &amp;lt;code&amp;gt;scripts/script_helpers.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;scripts/11_import_example.gram.py&amp;lt;/code&amp;gt; for a working example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from script_helpers import decade&lt;br /&gt;
&lt;br /&gt;
columns(&amp;quot;Decade&amp;quot;, &amp;quot;Births&amp;quot;)&lt;br /&gt;
counts = counter()&lt;br /&gt;
for person in people():&lt;br /&gt;
    birth = person.birth&lt;br /&gt;
    if birth:&lt;br /&gt;
        year = birth.get_date_object().get_year()&lt;br /&gt;
        if year:&lt;br /&gt;
            counts[decade(year)] += 1&lt;br /&gt;
&lt;br /&gt;
for decade_start, count in sorted(counts.items()):&lt;br /&gt;
    row(&amp;quot;%ds&amp;quot; % decade_start, count)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Record fields ==&lt;br /&gt;
Every record yielded by &amp;lt;code&amp;gt;people()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;active_person&amp;lt;/code&amp;gt;, and so on is a dict-like wrapper around the record's raw JSON data. All of that raw data's own fields are available as attributes (e.g. &amp;lt;code&amp;gt;person.gramps_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;person.handle&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;person.private&amp;lt;/code&amp;gt;), and in addition the wrapper exposes these computed, ready-to-use properties:&lt;br /&gt;
&lt;br /&gt;
::{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Property !! Available on !! Returns&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;gender&amp;lt;/code&amp;gt; || Person || Display string for the gender (&amp;quot;male&amp;quot;/&amp;quot;female&amp;quot;/&amp;quot;unknown&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;surname&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; || Person, others || Primary name / primary surname / all names&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;birth&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;death&amp;lt;/code&amp;gt; || Person || The Birth/Death Event record, or empty if none&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;age&amp;lt;/code&amp;gt; || Person || Age at death (birth to death), or empty if either date is missing&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;father&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mother&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;parents&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;spouse&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;children&amp;lt;/code&amp;gt; || Person || Related Person record(s)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;families&amp;lt;/code&amp;gt; || Person || Families this person is a parent in&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;parent_families&amp;lt;/code&amp;gt; || Person || Families this person is a child in&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;place&amp;lt;/code&amp;gt; || Event || The Place record for the event&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;source&amp;lt;/code&amp;gt; || Citation || The Source record for the citation&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;notes&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tags&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;citations&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;events&amp;lt;/code&amp;gt; || most records || The attached Note/Tag/Citation/Event records&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;attributes&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;addresses&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;references&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;lds_ords&amp;lt;/code&amp;gt; || Person || The raw attribute/address/person-reference/LDS-ordinance lists&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;back_references&amp;lt;/code&amp;gt; || any record || Every record that directly refers to this one&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;back_references_recursively&amp;lt;/code&amp;gt; || any record || Every record reachable by following references from this one, recursively&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A list-valued field (e.g. &amp;lt;code&amp;gt;person.citations&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;family.children&amp;lt;/code&amp;gt;) can itself be iterated, indexed, or have an attribute/property looked up across every item at once — e.g. &amp;lt;code&amp;gt;person.citations.source&amp;lt;/code&amp;gt; returns the list of Source records for all of that person's citations.&lt;br /&gt;
&lt;br /&gt;
== Bundled example scripts ==&lt;br /&gt;
The addon ships with a folder of example &amp;lt;code&amp;gt;.gram.py&amp;lt;/code&amp;gt; scripts, opened via {{man menu|Script -&amp;gt; Open...}}. It is also the default save location, so you can keep your own scripts there alongside the examples (an addon update only overwrites files that share a name with one of the numbered examples below, so anything you save under another name is safe).&lt;br /&gt;
&lt;br /&gt;
::{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! File !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;01_list_people.gram.py&amp;lt;/code&amp;gt; || List every person with ID, given name, surname, and gender.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;02_filter_by_surname.gram.py&amp;lt;/code&amp;gt; || List only people whose surname matches a given value.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;03_family_overview.gram.py&amp;lt;/code&amp;gt; || List every family with father, mother, and child count.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;04_gender_pie_chart.gram.py&amp;lt;/code&amp;gt; || Pie chart of the gender breakdown of everyone in the tree.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;05_age_histogram.gram.py&amp;lt;/code&amp;gt; || Histogram of age at death, for people with both birth and death recorded.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;06_mark_unsourced_people_private.gram.py&amp;lt;/code&amp;gt; || Batch-edit example: mark people with no citations as private.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;07_csv_ready_report.gram.py&amp;lt;/code&amp;gt; || Tabular report meant to be exported via {{man menu|Data -&amp;gt; Save as CSV}}.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;08_active_person_summary.gram.py&amp;lt;/code&amp;gt; || Summary of the active person plus their parents, spouse, and children.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;09_selected_people_report.gram.py&amp;lt;/code&amp;gt; || Report on just the rows currently selected in the People view.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;10_find_missing_birth_dates.gram.py&amp;lt;/code&amp;gt; || Data-quality check: people with no recorded birth event.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;11_import_example.gram.py&amp;lt;/code&amp;gt; || Counts births per decade using &amp;lt;code&amp;gt;decade()&amp;lt;/code&amp;gt;, imported from &amp;lt;code&amp;gt;script_helpers.py&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;12_custom_filter_example.gram.py&amp;lt;/code&amp;gt; || Runs one of your own custom filters by name via &amp;lt;code&amp;gt;custom_filter()&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;13_delete_unused_repositories.gram.py&amp;lt;/code&amp;gt; || Delete example: removes Repository records nothing refers to.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
* [https://pypi.org/project/jedi/ jedi] Python module — required for the addon to load at all (it powers [[#Tab_completion|Tab completion]]).&lt;br /&gt;
&lt;br /&gt;
Enable {{checkbox|1}}{{man label|Allow Gramps to install required python modules}} on the Addon Manager's Settings tab so Gramps installs it automatically when you install {{man label|Gram.py Script}}.&lt;br /&gt;
&lt;br /&gt;
== Credits ==&lt;br /&gt;
{{man label|Gram.py Script}} is a reimagining, for Gramps 6, of Kari Kujansuu's [[Addon:Isotammi_addons#SuperTool|SuperTool]] Gramplet, built by Doug Blank on top of Gramps 6's JSON-backed data dictionaries for speed.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[Addon:Isotammi_addons#SuperTool|SuperTool]] — the Isotammi addon that inspired {{man label|Gram.py Script}}.&lt;br /&gt;
* [https://github.com/gramps-project/addons-source/pull/666 New Gramplet: Gram.py Script] — the pull request that introduced this addon.&lt;br /&gt;
* [https://github.com/gramps-project/addons-source/tree/maintenance/gramps61/GrampyScript GrampyScript source] on GitHub.&lt;br /&gt;
&lt;br /&gt;
[[Category:Addons]]&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Gramplets]]&lt;br /&gt;
[[Category:Developers/General]]&lt;/div&gt;</summary>
		<author><name>Dsblank</name></author>
		
	</entry>
</feed>