This is a Third-party Addon. The Addon/Plugin system is controlled by the Plugin Manager. Please use carefully on data that is backed up, and help make it better by reporting any issues to the bug tracker. |
The 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 SuperTool Gramplet for Gramps 6, built directly on Gramps 6's JSON-backed data dictionaries so it can iterate over an entire tree quickly.
Contents
Usage
From the Dashboard use Add a gramplet and select 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).
The Gramplet has three parts:
- 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).
- An Execute <Alt+Enter> button.
- A results area below, with three tabs: Table, Output, and Chart.
Press Execute <Alt+Enter> (or the Alt+↵ Enter keyboard shortcut) to run the script currently in the editor. Whichever tab has something to show is switched to automatically: the Table tab if the script called row(), the Chart tab if it called chart(), otherwise the Output tab (also used for tracebacks, when a script raises an exception).
Tab completion
Tab completion (and the addon itself) requires the jedi Python module — see Prerequisites below. |
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 Gram.py Script DSL described below (people, active_person, and so on), including the fields and computed properties of the record wrapper once you type a dot, e.g. person.. Use ↑/↓ to move the selection, Tab ⇆ or ↵ Enter to accept it, and Esc or any arrow/Home/End/Page key to dismiss the popup without accepting.
Table, Output, and Chart tabs
- Table — one row per call to
row(...)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. - Output — anything printed with
print(), plus the Python traceback if the script raised an exception, and a line reporting the elapsed execution time. - Chart — the drawing made by the last
chart()call in the script.
- Script -> New — clear the editor.
- Script -> Open... — open a
.gram.pyfile. The dialog defaults to this addon's bundledscriptsfolder (see below) and shows each file's title/description as a preview when highlighted. - Script -> Save (CTRL+S) — save over the currently open file.
- Script -> Save as... — save the current editor contents to a new
.gram.pyfile.
The last file you had open is remembered and reloaded automatically the next time you open the Gramplet.
Once a script has produced a Table:
- Data -> 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.
- Data -> Copy to clipboard — copy the table's rows to the clipboard as tab-separated text, ready to paste into a spreadsheet.
Keyboard shortcuts (while editing)
Shortcut Action Alt+↵ Enter Execute the script Tab ⇆ Open completion popup (or insert 4 spaces if nothing to complete) CTRL+Z Undo CTRL+⇧ Shift+Z Redo CTRL+X / CTRL+V Cut / Paste Alt+C Copy CTRL+S Save script
Writing scripts
A script is plain Python, executed in a namespace that additionally provides the names described below. The simplest possible script:
for person in people():
row(person.gramps_id, person.name.first_name, person.surname.surname, person.gender)
Iterating over records
Each of these is a zero-argument generator yielding one wrapped record (see Record fields below) per item in the tree:
people(),families(),events(),places(),notes(),media(),sources(),citations(),repositories()
Three more give you a subset instead of everything:
selected("Person")— only the rows currently selected (highlighted) in that category's list view. The argument is the table/category name ("Person","Family","Event", ...).filtered("Person")— every row currently visible in that category's list view (i.e. after whatever filter/search is applied there).custom_filter(name, namespace="Person")— 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 Output tab and nothing is yielded.
The active record
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:
active_person,active_family,active_event,active_place,active_note,active_media,active_source,active_citation,active_repository
When nothing is active, these (and any missing field on a real record) evaluate to a NoneData placeholder: it prints as an empty string, is falsy in an if, and any attribute access or call on it just returns another empty NoneData — so active_person.birth.place.name is always safe to write even with nothing selected.
Building the results table
row(*values)— add one row to the Table tab. The number of arguments determines the number of columns; column widths and sorting are automatic (numbers sort numerically).columns(*names)— 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 torow().
Charts
chart(type, data, count=20, **kwargs) draws on the Chart tab. type is one of:
"pie"or"bar"—datais adictof{label: count}(or a list of[label, count]pairs); only the topcountentries by count are drawn."histogram"—datais a plain list of numbers, bucketed intocountintervals. Passdecimal_places=Nas a keyword argument to control how the bucket-range labels are formatted.
counter() returns a fresh collections.defaultdict(int) — a convenient accumulator for building the data a chart or CSV report needs, e.g. counts[person.gender] += 1.
Editing and deleting data
begin_changes(message="Gram.py Script Edited Data")/end_changes()— wrap a batch of edits in a single undoable transaction, so Edit -> Undo reverts the whole batch in one step instead of one step per record. If a script errors out or you forgetend_changes(), the Gramplet closes the transaction automatically once the script finishes.- Setting an attribute on a record, e.g.
person.private = True, or calling one of itsset_*()methods, e.g.person.set_privacy(True), writes the change straight to the database (inside whatever transaction is open). delete(record)— permanently remove a record (e.g.delete(repository)). There is no separate confirmation step, so checkrecord.back_referencesis empty first if you only want to remove records nothing else refers to.
Other helpers
today— a GrampsDateobject for today's date.print(...)— writes to the Output tab instead of a terminal.database— the raw Gramps database object (self.dbstate.db), for anything not covered by the helpers above.
Importing your own helper code
A script can import a plain .py module the normal way, as long as that module lives next to the script's own .gram.py file, or in the bundled scripts folder. This is a convenient way to share functions between several of your own scripts instead of copy-pasting them. See scripts/script_helpers.py and scripts/11_import_example.gram.py for a working example:
from script_helpers import decade
columns("Decade", "Births")
counts = counter()
for person in people():
birth = person.birth
if birth:
year = birth.get_date_object().get_year()
if year:
counts[decade(year)] += 1
for decade_start, count in sorted(counts.items()):
row("%ds" % decade_start, count)
Record fields
Every record yielded by people(), active_person, 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. person.gramps_id, person.handle, person.private), and in addition the wrapper exposes these computed, ready-to-use properties:
Property Available on Returns genderPerson Display string for the gender ("male"/"female"/"unknown") name/surname/namesPerson, others Primary name / primary surname / all names birth/deathPerson The Birth/Death Event record, or empty if none agePerson Age at death (birth to death), or empty if either date is missing father/mother/parents/spouse/childrenPerson Related Person record(s) familiesPerson Families this person is a parent in parent_familiesPerson Families this person is a child in placeEvent The Place record for the event sourceCitation The Source record for the citation notes/tags/citations/eventsmost records The attached Note/Tag/Citation/Event records attributes/addresses/references/lds_ordsPerson The raw attribute/address/person-reference/LDS-ordinance lists back_referencesany record Every record that directly refers to this one back_references_recursivelyany record Every record reachable by following references from this one, recursively
A list-valued field (e.g. person.citations, family.children) can itself be iterated, indexed, or have an attribute/property looked up across every item at once — e.g. person.citations.source returns the list of Source records for all of that person's citations.
Bundled example scripts
The addon ships with a folder of example .gram.py scripts, opened via Script -> 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).
File Description 01_list_people.gram.pyList every person with ID, given name, surname, and gender. 02_filter_by_surname.gram.pyList only people whose surname matches a given value. 03_family_overview.gram.pyList every family with father, mother, and child count. 04_gender_pie_chart.gram.pyPie chart of the gender breakdown of everyone in the tree. 05_age_histogram.gram.pyHistogram of age at death, for people with both birth and death recorded. 06_mark_unsourced_people_private.gram.pyBatch-edit example: mark people with no citations as private. 07_csv_ready_report.gram.pyTabular report meant to be exported via Data -> Save as CSV. 08_active_person_summary.gram.pySummary of the active person plus their parents, spouse, and children. 09_selected_people_report.gram.pyReport on just the rows currently selected in the People view. 10_find_missing_birth_dates.gram.pyData-quality check: people with no recorded birth event. 11_import_example.gram.pyCounts births per decade using decade(), imported fromscript_helpers.py.12_custom_filter_example.gram.pyRuns one of your own custom filters by name via custom_filter().13_delete_unused_repositories.gram.pyDelete example: removes Repository records nothing refers to.
Prerequisites
- jedi Python module — required for the addon to load at all (it powers Tab completion).
Enable
Allow Gramps to install required python modules on the Addon Manager's Settings tab so Gramps installs it automatically when you install Gram.py Script.
Credits
Gram.py Script is a reimagining, for Gramps 6, of Kari Kujansuu's SuperTool Gramplet, built by Doug Blank on top of Gramps 6's JSON-backed data dictionaries for speed.
See also
- SuperTool — the Isotammi addon that inspired Gram.py Script.
- New Gramplet: Gram.py Script — the pull request that introduced this addon.
- GrampyScript source on GitHub.
