New Gram.py Script for 6.0

I really like the idea of the SuperTool script-like things. But there were somethings that might be done differently now with Gramps 6.0. And I have some ideas. So I thought I’d take a stab at reimagining portable scripts for doing things in Gramps. And thus Gram.py Script gramplet was born.

First, it was just added to source-addons so isn’t published yet. (I’ll also note that this is PR #666, so beware).

The UI looks like this:

Some major differences between this and SuperTools:

  1. Everything is contained in the source code, and the code is pure Python. There is nothing else.
  2. You can’t make filters with Gram.py.
  3. Gram.py is a gramplet, whereas SuperTool was a tool
  4. Gram.py is not sensitive to what view you are on
  5. There are three main things you can do:
    • create tables out of arbitrary expressions
    • create charts, including histogram, bar, and pie
    • edit data

Some similar things with SuperTool:

  1. items on tabular view can be double-clicked to bring up editor
  2. can save and load scripts
  3. there are aliases to cut-down on the code
  4. I used some of the code from SuperTools, like save/load dialogs

Some enhancements:

  1. Full undo/redo support in editor
  2. Syntax highlighting
  3. Some customized keys for editing (Alt+Enter, Alt+C, Tab, etc.)

Here are some code examples:

for person in people():
    row(person)

Creates a tabular view of all people.

for person in people():
    if person.gender == "male":
        row(person)

Just the boys.

Charts:

data = counter()
for person in people():
    data[person.gender] += 1
chart("pie", data)

data = []
for person in people():
    if person.age:
        years = tuple(person.age)[0]
        if 0 < years <= 110:
            data.append(years)
chart("histogram", data, 10)

There is more to describe, but I thought I test the reception of this, and also request feedback and ideas. Do you like the name?

2 Likes

Thank you for the preview!

Could you give an example of the histogram using custom filtered (or view contents) data?

1 Like

Sure!

For those that are selected (highlighted in the view):

for person in selected("Person"):
    row(person)

For those that are filtered in the view:

for person in filtered("Person"):
    row(person)

The histogram stuff would be the same.

2 Likes

Now ready for installation on Gramps 6.0. Challenge: give me a task and let’s see if Gram.py can do it!

Can Gram.py list keys and values (attributes) on Citation objects, then group by these keys? Same idea around associations on Individuals, maybe like a relations graph!

Like this?

for person in people():
    for citation in person.citations:
        for attribute in citation.attributes:
            row(person, 
                citation, 
                attribute.type.string, 
                attribute.value
        )

Sounds great!

Even so, is it aware of the currently active object(s) of each type, if any?

Meaning, edit existing objects, not create new ones?

Yes, all of the active objects are defined (if a view is loaded), like active_person, active_note, etc.

Wow… interesting. Are seriously thinking about doing that via a script? I would think the CSV Import through the Text Import would be easier. But let me know if you are serious, and the use-case.

Oh, it is oriented individuals, like the gedcom. It was rather a test for the challenge… I know that Note are not really (or always!) primary object and that the citation objects are (I do not know the expression in English) like an “Exception”. I rather thought as Citation as the top level. Something like a starting point (Citation → Sources → Repositories → Individuals → Events → Relations , then an infinite loop…)

Anyway, the same idea could be around attributes, adresses or associations, … into all people.

for person in people():
    for attribute in person.attributes:
            row(person, 
                ?, 
                attribute.type.string, 
                attribute.value
        )

I know, to retrieve data from secondary objects might be a challenge…

Like this?

for person in people():
    for ref in person.references:
        row(person, ref.rel, ref.reference)

You get my point… :wink:

https://gramps.discourse.group/t/could-you-add-a-context-menu-to-the-association-state-tool-to-open-object-editors/5351

You can do that :slight_smile:

for citation in citations():
    source = citation.source
    for repo in source.repositories:
        for note in repo.notes:
            row(note)

You can get backreferences, just by setting the parent object?
Does ‘repositories’ exist as a new built-in (hardcoded?) list of objects?
This might deprecate (deprecating?) some very old addons, like for this output:

There are some short-cut/aliases for some items (like person.name is just short-cut for person.primary_name, and person.names is short for [person.primary_name] + person.alternate_names]) and some alias for looking up an object, like person.mother (uses the SimpleAccess methods), but all of the real object attributes are also there.

All items are lazily loaded, and most use fast data lookups (without making objects if possible).

If code works in 6.0, it should work here. :crossed_fingers:

Yes, proper and very quick! :clap:
Can you go farther?
Make a relationships graph with such type of groups


Most above columns are extra stuff (can skip them), but the idea behind this addon was to group (order) according to distance to the active (or root) person. A simple representation of the relationship calculator but with columns and grouping feature.

Backreferences, in a new PR:

for item in active_person.back_references:
    row(item)

You might want get_referenced_handles_recursively as an alternative to back_references

See Need to add the References tab to Family window - #7 by emyoulation

Relationship list:

from gramps.gen.relationship import get_relationship_calculator

rc = get_relationship_calculator()
columns("Relative", "Relationship, of", "Active Person", "Dist org", "Dist other")
for person in people():
    (relationship, _ga, _gb) = rc.get_one_relationship(
        self.db,
        active_person,
        person,
        extra_info=True,
    )
    if relationship:
        row(person, relationship, active_person, _ga, _gb)

2 Likes

Added: person.back_references_recursively:

for item in active_person.back_references_recursively:
    row(item)

I would like an easy way to “clone” things. For example, I have a new source that is very similar to an existing source (say, a census). Ideally I could right-click on the existing source and select “Clone”. But running a script that clones the active source would be fine. The script could prepend "[Clone of] " or something like that in the title. Ideally the script would also create a repository reference for the new script, matching the old one (just for the Repository, not for things like Call Number).

A similar example is cloning an existing citation to create a new citation for the same source, leaving the Volume/Page and Date blank.

Though I would still need to edit the newly created source or citation, it seems like it would save me a lot of clicks in selecting the repository or source.

Could such a script also display a dialog with check boxes etc., so that I could refine what it does each time?