Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added db.select_from_TABLE methods #1828

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7a8afc5
Added a dict wrapper that acts like an object
dsblank Dec 11, 2024
4fc46b7
Linting
dsblank Dec 11, 2024
f86bc4c
Convert to object if str(data)
dsblank Dec 14, 2024
771cdd2
Linting
dsblank Dec 14, 2024
de3e0a8
Added 11k tests
dsblank Dec 19, 2024
1f0287d
Added a version of select using string+ast
dsblank Dec 21, 2024
1491bf9
Linting
dsblank Dec 22, 2024
5647610
Add gen.db.conversion_tools from PR #1786
Nick-Hall Dec 8, 2024
ecfd747
Convert to object if str(data)
dsblank Dec 14, 2024
768e608
Merge branch 'master' into dsb/added-select-via-ast
dsblank Dec 22, 2024
ce9cfeb
Added select what, added to generic
dsblank Dec 26, 2024
9c44a58
Added DBGeneric.select_from_table fallback
dsblank Dec 30, 2024
f30a659
Return Primary Objects, rather than raw data
dsblank Dec 30, 2024
522cb2c
Hide select_from_table; added docs
dsblank Dec 31, 2024
5f2b31e
Fixed two bugs: IN, and return OBJECT
dsblank Jan 1, 2025
2731f58
Added '_' as object; added len(person.family_list)
dsblank Jan 1, 2025
14e681f
WIP: adding tests
dsblank Jan 2, 2025
8ec3701
More tests
dsblank Jan 3, 2025
39750bf
Test DbGeneric
dsblank Jan 3, 2025
652fe64
Always use table_name for _
dsblank Jan 3, 2025
e0dc3f0
Use correct quotes for Python 3.9 sqlite
dsblank Jan 3, 2025
9577897
Bumping CI to ubuntu 21.04
dsblank Jan 3, 2025
e785303
Bumping CI to ubuntu 22.04
dsblank Jan 3, 2025
77ec49c
Adjust package names for ubuntu-22.04
dsblank Jan 3, 2025
f54d6d9
Adjust package names for ubuntu-22.04
dsblank Jan 3, 2025
c917535
Show SQL command on error
dsblank Jan 3, 2025
fdecf87
Change syntax of order_by: '-person.gender'
dsblank Jan 5, 2025
5804e3a
Print out sqlite versions
dsblank Jan 5, 2025
1ecb3bf
Skip tests if no support for json_array_length
dsblank Jan 6, 2025
f6b4c5f
Install pytest
dsblank Jan 6, 2025
dbfab2e
Try a variation of json_array_length
dsblank Jan 7, 2025
1cdd63a
Unroll json_extract
dsblank Jan 7, 2025
5204a60
Put everything back
dsblank Jan 7, 2025
93529ef
Finished adding tests
dsblank Jan 8, 2025
939c968
Removed comment
dsblank Jan 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/tests/example.gramps
Original file line number Diff line number Diff line change
Expand Up @@ -18213,7 +18213,7 @@
<childof hlink="_DLTJQCAPOXEIKSOU3J"/>
<citationref hlink="_c140d24638d5368e8e4"/>
</person>
<person handle="_0GDKQC54XKSWZKEBWW" change="1185438865" id="I0988">
<person handle="_0GDKQC54XKSWZKEBWW" change="1185438865" id="I0988" priv="1">
<gender>M</gender>
<name type="Birth Name">
<first>John</first>
Expand Down
7 changes: 7 additions & 0 deletions gramps/gen/db/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ def __init__(self):
self.basedb = self
self.__feature = {} # {"feature": VALUE, ...}

def is_proxy(self):
"""
Returns True if the database has a proxy, such as Living, or Private.
Otherwise, return False.
"""
return self != self.basedb

def get_feature(self, feature):
"""
Databases can implement certain features or not. The default is
Expand Down
167 changes: 167 additions & 0 deletions gramps/gen/db/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
from .bookmarks import DbBookmarks
from .exceptions import DbUpgradeRequiredError, DbVersionError
from .utils import clear_lock_file, write_lock_file
from .select import select_from_table

_ = glocale.translation.gettext

Expand Down Expand Up @@ -2739,3 +2740,169 @@ def set_serializer(self, serializer_name):
self.serializer = BlobSerializer
elif serializer_name == "json":
self.serializer = JSONSerializer

def _select_from_table(
self,
table_name,
what=None,
where=None,
order_by=None,
env=None,
):
"""
Select items from table_name where python-string is True,
optionally with a list of python-string items to order on.

This method should be overridden in suclasses for speed, but
used as a fallback when on a proxy database.

Examples:

db.select_from_person(where="person.handle == 'A6E74B3D65D23F'")
db.select_from_person("person.handle", where="person.handle == 'A6E74B3D65D23F'")
db.select_from_person("_.handle", where="_.handle == 'A6E74B3D65D23F'")
db.select_from_person(
what=["person.handle", "person.gramps_id"],
where="person.handle == 'A6E74B3D65D23F'"
order_by=["-person.gramps_id", "person.gender"]
env={"Person": Person}
)
"""
yield from select_from_table(self, table_name, what, where, order_by, env)

def select_from_citation(self, what=None, where=None, order_by=None, env=None):
"""
Select items from citation where python-string is True,
optionally with a list of python-string items to order on.

Example:

db.select_from_citation(where="citation.handle == 'A6E74B3D65D23F'")
"""
yield from self._select_from_table(
"citation", what=what, where=where, order_by=order_by, env=env
)

def select_from_event(self, what=None, where=None, order_by=None, env=None):
"""
Select items from event where python-string is True,
optionally with a list of python-string items to order on.

Example:

db.select_from_event(where="event.handle == 'A6E74B3D65D23F'")
"""
yield from self._select_from_table(
"event", what=what, where=where, order_by=order_by, env=env
)

def select_from_family(self, what=None, where=None, order_by=None, env=None):
"""
Select items from family where python-string is True,
optionally with a list of python-string items to order on.

Example:

db.select_from_family(where="family.handle == 'A6E74B3D65D23F'")
"""
yield from self._select_from_table(
"family", what=what, where=where, order_by=order_by, env=env
)

def select_from_media(self, what=None, where=None, order_by=None, env=None):
"""
Select items from media where python-string is True,
optionally with a list of python-string items to order on.

Example:

db.select_from_media(where="media.handle == 'A6E74B3D65D23F'")
"""
yield from self._select_from_table(
"media", what=what, where=where, order_by=order_by, env=env
)

def select_from_note(self, what=None, where=None, order_by=None, env=None):
"""
Select items from note where python-string is True,
optionally with a list of python-string items to order on.

Example:

db.select_from_note(where="note.handle == 'A6E74B3D65D23F'")
"""
yield from self._select_from_table(
"note", what=what, where=where, order_by=order_by, env=env
)

def select_from_person(self, what=None, where=None, order_by=None, env=None):
"""
Select items from person where python-string is True,
optionally with a list of python-string items to order on.

Examples:

db.select_from_person(where="person.handle == 'A6E74B3D65D23F'")
db.select_from_person("person.handle", where="person.handle == 'A6E74B3D65D23F'")
db.select_from_person(
what=["person.handle", "person.gramps_id"],
where="person.handle == 'A6E74B3D65D23F'"
order_by=[("person.gramps_id", "DESC")]
env={"Person": Person}
)
"""
yield from self._select_from_table(
"person", what=what, where=where, order_by=order_by, env=env
)

def select_from_place(self, what=None, where=None, order_by=None, env=None):
"""
Select items from place where python-string is True,
optionally with a list of python-string items to order on.

Examples:

db.select_from_place(where="place.handle == 'A6E74B3D65D23F'")
"""
yield from self._select_from_table(
"place", what=what, where=where, order_by=order_by, env=env
)

def select_from_repository(self, what=None, where=None, order_by=None, env=None):
"""
Select items from repository where python-string is True,
optionally with a list of python-string items to order on.

Examples:

db.select_from_repository(where="repository.handle == 'A6E74B3D65D23F'")
"""
yield from self._select_from_table(
"repository", what=what, where=where, order_by=order_by, env=env
)

def select_from_source(self, what=None, where=None, order_by=None, env=None):
"""
Select items from source where python-string is True,
optionally with a list of python-string items to order on.

Example:

db.select_from_source(where="source.handle == 'A6E74B3D65D23F'")
"""
yield from self._select_from_table(
"source", what=what, where=where, order_by=order_by, env=env
)

def select_from_tag(self, what=None, where=None, order_by=None, env=None):
"""
Select items from tag where python-string is True,
optionally with a list of python-string items to order on.

Example:

db.select_from_tag(where="tag.handle == 'A6E74B3D65D23F'")
"""
yield from self._select_from_table(
"tag", what=what, where=where, order_by=order_by, env=env
)
102 changes: 102 additions & 0 deletions gramps/gen/db/select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2024 Doug Blank <doug.blank@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

from gramps.gen.lib import (
Citation,
Event,
Family,
Media,
Note,
Person,
Place,
Repository,
Source,
Tag,
)


def sort_data(rows, specs):
for key, reverse in reversed(specs):
rows.sort(key=lambda item: item[key], reverse=reverse)
return rows


def select_from_table(db, table_name, what, where, order_by, env):
if order_by is None:
yield from _select_from_table(db, table_name, what=what, where=where, env=env)
return
else:
data = []

for row in _select_from_table(db, table_name, what=what, where=where, env=env):
if what is None:
what_expr = ["person"]
elif isinstance(what, str):
what_expr = [what]
else:
what_expr = what
data.append(dict(zip(what_expr, row)))

specs = []
for item in order_by:
if item.startswith("-"):
specs.append((item[1:], True))
else:
specs.append((item, False))

for row in sort_data(data, specs):
yield list(row.values())
return


def _select_from_table(db, table_name, what, where, env):
env = (
env
if env
else {
"Citation": Citation,
"Event": Event,
"Family": Family,
"Media": Media,
"Note": Note,
"Person": Person,
"Place": Place,
"Repository": Repository,
"Source": Source,
"Tag": Tag,
}
)
method = db._get_table_func(table_name.title(), "iter_func")
for obj in method():
env["_"] = env[table_name] = obj
if where:
where_value = eval(where, env)
else:
where_value = True

if where_value:
if what is None:
what_value = table_name
elif isinstance(what, str):
what_value = eval(what, env)
else:
what_value = [eval(w, env) for w in what]

yield what_value
5 changes: 2 additions & 3 deletions gramps/gen/filters/rules/test/person_rules_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1204,17 +1204,16 @@ def test_peopleprivate(self):
"""
Test PeoplePrivate rule.
"""
# TODO: example.gramps has no people marked private
rule = PeoplePrivate([])
self.assertEqual(self.filter_with_rule(rule), set([]))
self.assertEqual(self.filter_with_rule(rule), set(["0GDKQC54XKSWZKEBWW"]))

def test_peoplepublic(self):
"""
Test PeoplePublic rule.
"""
rule = PeoplePublic([])
# too many to list out to test explicitly
self.assertEqual(len(self.filter_with_rule(rule)), 2128)
self.assertEqual(len(self.filter_with_rule(rule)), 2127)

def test_personwithincompleteevent(self):
"""
Expand Down
Loading
Loading