diff --git a/gramps/gen/db/__init__.py b/gramps/gen/db/__init__.py index 5154ddf277b..f05b4108ad8 100644 --- a/gramps/gen/db/__init__.py +++ b/gramps/gen/db/__init__.py @@ -71,3 +71,5 @@ from .undoredo import * from .utils import * from .generic import * + +Database = DbGeneric diff --git a/gramps/gen/db/generic.py b/gramps/gen/db/generic.py index 96f85a6a1aa..06b4fe0a007 100644 --- a/gramps/gen/db/generic.py +++ b/gramps/gen/db/generic.py @@ -4,6 +4,7 @@ # Copyright (C) 2015-2016 Gramps Development Team # Copyright (C) 2016 Nick Hall # Copyright (C) 2024 Doug Blank +# Copyright (C) 2024,2025 Steve Youngs # # 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 @@ -2782,3 +2783,6 @@ def set_serializer(self, serializer_name): self.serializer = BlobSerializer elif serializer_name == "json": self.serializer = JSONSerializer + + +Database = DbGeneric diff --git a/gramps/gen/filters/_genericfilter.py b/gramps/gen/filters/_genericfilter.py index 75698e2bcac..b93eceacfa5 100644 --- a/gramps/gen/filters/_genericfilter.py +++ b/gramps/gen/filters/_genericfilter.py @@ -24,6 +24,9 @@ Package providing filtering framework for Gramps. """ +import logging +import time + # ------------------------------------------------------------------------ # # Gramps imports @@ -40,8 +43,11 @@ from ..lib.note import Note from ..lib.tag import Tag from ..const import GRAMPS_LOCALE as glocale +from .rules import Rule +from .optimizer import Optimizer _ = glocale.translation.gettext +LOG = logging.getLogger(".filter.results") # ------------------------------------------------------------------------- @@ -52,7 +58,7 @@ class GenericFilter: """Filter class that consists of several rules.""" - logical_functions = ["or", "and", "xor", "one"] + logical_functions = ["and", "or", "one"] def __init__(self, source=None): if source: @@ -74,10 +80,8 @@ def match(self, handle, db): """ Return True or False depending on whether the handle matches the filter. """ - if self.apply(db, [handle]): - return True - else: - return False + obj = self.get_object(handle) + return self.apply_to_one(db, obj) def is_empty(self): return (len(self.flist) == 0) or ( @@ -88,10 +92,7 @@ def set_logical_op(self, val): if val in GenericFilter.logical_functions: self.logical_op = val else: - self.logical_op = "and" - - def get_logical_op(self): - return self.logical_op + raise Exception("invalid operator: %r" % val) def set_invert(self, val): self.invert = bool(val) @@ -138,99 +139,116 @@ def find_from_handle(self, db, handle): def get_number(self, db): return db.get_number_of_people() - def check_func(self, db, id_list, task, user=None, tupleind=None, tree=False): + def apply_logical_op_to_all( + self, db, id_list, apply_logical_op, user=None, tupleind=None, tree=False + ): final_list = [] - if user: - user.begin_progress(_("Filter"), _("Applying ..."), self.get_number(db)) + + optimizer = Optimizer(self) + handles_in, handles_out = optimizer.get_handles() + + LOG.debug( + "Optimizer handles_in: %s", + len(handles_in) if handles_in is not None else None, + ) + LOG.debug("Optimizer handles_out: %s", len(handles_out)) if id_list is None: - with self.get_tree_cursor(db) if tree else self.get_cursor(db) as cursor: - for handle, data in cursor: - person = db.serializer.data_to_object(data) + if handles_in is not None: + if user: + user.begin_progress(_("Filter"), _("Applying ..."), len(handles_in)) + + # Use these rather than going through entire database + for handle in handles_in: if user: user.step_progress() - if task(db, person) != self.invert: - final_list.append(handle) + + if handle is None: + continue + + obj = self.get_object(db, handle) + + if apply_logical_op(db, obj, self.flist) != self.invert: + final_list.append(obj.handle) + + else: + with ( + self.get_tree_cursor(db) if tree else self.get_cursor(db) + ) as cursor: + if user: + user.begin_progress( + _("Filter"), _("Applying ..."), self.get_number(db) + ) + + for handle, obj in cursor: + if user: + user.step_progress() + + if handle in handles_out: + continue + + if apply_logical_op(db, obj, self.flist) != self.invert: + final_list.append(handle) + else: - for data in id_list: - if tupleind is None: - handle = data - else: - handle = data[tupleind] - person = self.find_from_handle(db, handle) + if user: + id_list = list(id_list) + user.begin_progress(_("Filter"), _("Applying ..."), len(id_list)) + for handle_data in id_list: if user: user.step_progress() - if task(db, person) != self.invert: - final_list.append(data) - if user: - user.end_progress() - return final_list - def check_and(self, db, id_list, user=None, tupleind=None, tree=False): - final_list = [] - flist = self.flist - if user: - user.begin_progress(_("Filter"), _("Applying ..."), self.get_number(db)) - if id_list is None: - with self.get_tree_cursor(db) if tree else self.get_cursor(db) as cursor: - for handle, data in cursor: - person = db.serializer.data_to_object(data) - if user: - user.step_progress() - val = all(rule.apply(db, person) for rule in flist) - if val != self.invert: - final_list.append(handle) - else: - for data in id_list: if tupleind is None: - handle = data + handle = handle_data else: - handle = data[tupleind] - person = self.find_from_handle(db, handle) - if user: - user.step_progress() - val = all(rule.apply(db, person) for rule in flist if person) - if val != self.invert: - final_list.append(data) - if user: - user.end_progress() - return final_list + handle = handle_data[tupleind] - def check_or(self, db, id_list, user=None, tupleind=None, tree=False): - return self.check_func(db, id_list, self.or_test, user, tupleind, tree=False) + if handles_in is not None: + if handle not in handles_in: + continue + elif handle in handles_out: + continue - def check_one(self, db, id_list, user=None, tupleind=None, tree=False): - return self.check_func(db, id_list, self.one_test, user, tupleind, tree=False) + obj = self.get_object(db, handle) - def check_xor(self, db, id_list, user=None, tupleind=None, tree=False): - return self.check_func(db, id_list, self.xor_test, user, tupleind, tree=False) + if apply_logical_op(db, obj, self.flist) != self.invert: + final_list.append(handle_data) - def xor_test(self, db, person): - test = False - for rule in self.flist: - test = test ^ rule.apply(db, person) - return test + if user: + user.end_progress() + + return final_list - def one_test(self, db, person): + def and_test(self, db, data: dict, flist): + return all(rule.apply_to_one(db, data) for rule in flist) + + def one_test(self, db, data: dict, flist): found_one = False - for rule in self.flist: - if rule.apply(db, person): + for rule in flist: + if rule.apply_to_one(db, data): if found_one: return False # There can be only one! found_one = True return found_one - def or_test(self, db, person): - return any(rule.apply(db, person) for rule in self.flist) + def or_test(self, db, data: dict, flist): + return any(rule.apply_to_one(db, data) for rule in flist) - def get_check_func(self): - try: - m = getattr(self, "check_" + self.logical_op) - except AttributeError: - m = self.check_and - return m + def get_logical_op(self): + return self.logical_op - def check(self, db, handle): - return self.get_check_func()(db, [handle]) + def apply_to_one(self, db, data: dict) -> bool: + """ + Filter-level apply rules to single data item. + """ + if self.logical_op == "and": + res = self.and_test(db, data, self.flist) + elif self.logical_op == "or": + res = self.or_test(db, data, self.flist) + elif self.logical_op == "one": + res = self.one_test(db, data, self.flist) + else: + raise Exception("invalid operator: %r" % self.logical_op) + return res != self.invert def apply(self, db, id_list=None, tupleind=None, user=None, tree=False): """ @@ -249,14 +267,44 @@ def apply(self, db, id_list=None, tupleind=None, user=None, tree=False): if id_list not given, all items in the database that match the filter are returned as a list of handles """ - m = self.get_check_func() + if user: + user.begin_progress(_("Filter"), _("Preparing ..."), len(self.flist) + 1) + # FIXME: this dialog doesn't show often. Adding a time.sleep(0.1) here + # can help on my machine + + start_time = time.time() for rule in self.flist: + if user: + user.step_progress() rule.requestprepare(db, user) - res = m(db, id_list, user, tupleind, tree) + LOG.debug("Prepare time: %s seconds", time.time() - start_time) + + if user: + user.end_progress() + + if self.logical_op == "and": + apply_logical_op = self.and_test + elif self.logical_op == "or": + apply_logical_op = self.or_test + elif self.logical_op == "one": + apply_logical_op = self.one_test + else: + raise Exception("invalid operator: %r" % self.logical_op) + + start_time = time.time() + res = self.apply_logical_op_to_all( + db, id_list, apply_logical_op, user, tupleind, tree + ) + LOG.debug("Apply time: %s seconds", time.time() - start_time) + for rule in self.flist: rule.requestreset() + return res + def get_object(self, db, handle): + return db.get_person_from_handle(handle) + class GenericFamilyFilter(GenericFilter): def __init__(self, source=None): @@ -274,6 +322,9 @@ def find_from_handle(self, db, handle): def get_number(self, db): return db.get_number_of_families() + def get_object(self, db, handle): + return db.get_family_from_handle(handle) + class GenericEventFilter(GenericFilter): def __init__(self, source=None): @@ -291,6 +342,9 @@ def find_from_handle(self, db, handle): def get_number(self, db): return db.get_number_of_events() + def get_object(self, db, handle): + return db.get_event_from_handle(handle) + class GenericSourceFilter(GenericFilter): def __init__(self, source=None): @@ -308,6 +362,9 @@ def find_from_handle(self, db, handle): def get_number(self, db): return db.get_number_of_sources() + def get_object(self, db, handle): + return db.get_source_from_handle(handle) + class GenericCitationFilter(GenericFilter): def __init__(self, source=None): @@ -328,6 +385,9 @@ def find_from_handle(self, db, handle): def get_number(self, db): return db.get_number_of_citations() + def get_object(self, db, handle): + return db.get_citation_from_handle(handle) + class GenericPlaceFilter(GenericFilter): def __init__(self, source=None): @@ -348,6 +408,9 @@ def find_from_handle(self, db, handle): def get_number(self, db): return db.get_number_of_places() + def get_object(self, db, handle): + return db.get_place_from_handle(handle) + class GenericMediaFilter(GenericFilter): def __init__(self, source=None): @@ -365,6 +428,9 @@ def find_from_handle(self, db, handle): def get_number(self, db): return db.get_number_of_media() + def get_object(self, db, handle): + return db.get_media_from_handle(handle) + class GenericRepoFilter(GenericFilter): def __init__(self, source=None): @@ -382,6 +448,9 @@ def find_from_handle(self, db, handle): def get_number(self, db): return db.get_number_of_repositories() + def get_object(self, db, handle): + return db.get_repository_from_handle(handle) + class GenericNoteFilter(GenericFilter): def __init__(self, source=None): @@ -399,6 +468,9 @@ def find_from_handle(self, db, handle): def get_number(self, db): return db.get_number_of_notes() + def get_object(self, db, handle): + return db.get_note_from_handle(handle) + def GenericFilterFactory(namespace): if namespace == "Person": diff --git a/gramps/gen/filters/optimizer.py b/gramps/gen/filters/optimizer.py new file mode 100644 index 00000000000..32694d24186 --- /dev/null +++ b/gramps/gen/filters/optimizer.py @@ -0,0 +1,137 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2024 Doug Blank +# +# 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. +# + +import logging + +LOG = logging.getLogger(".filter.optimizer") + + +def intersection(sets): + if sets: + result = sets[0] + for s in sets[1:]: + result = result.intersection(s) + return result + else: + return set() + + +def union(sets): + if sets: + result = sets[0] + for s in sets[1:]: + result = result.union(s) + return result + else: + return set() + + +class Optimizer: + """ + Optimizer to use the filter's pre-selected selected_handles + to include or exclude. + """ + + def __init__(self, filter): + """ + Initialize the collection of selected_handles in the filter list. + """ + self.all_selected_handles = [] + self.walk_filters(filter, False, self.all_selected_handles) + + def walk_filters(self, filter, parent_invert, result): + """ + Recursively walk all of the filters/rules and get + rules with selected_handles + """ + current_invert = parent_invert if not filter.invert else not parent_invert + LOG.debug( + "walking, filter: %s, invert=%s, parent_invert=%s", + filter, + filter.invert, + parent_invert, + ) + rules_with_selected_handles = [] + for item in filter.flist: + if hasattr(item, "find_filter"): + rule_filter = item.find_filter() + if rule_filter is not None: + self.walk_filters( + rule_filter, + current_invert, + result, + ) + elif hasattr(item, "selected_handles"): + rules_with_selected_handles.append(set(item.selected_handles)) + if rules_with_selected_handles: + LOG.debug( + "filter %s: parent_invert=%s, invert=%s, op=%s, number of rules with selected_handles=%s", + filter, + parent_invert, + filter.invert, + filter.logical_op, + len(rules_with_selected_handles), + ) + result.append( + ( + current_invert, + filter.logical_op, + rules_with_selected_handles, + ) + ) + + def get_handles(self): + """ + Returns handles_in, and handles_out. + + `handles_in` is either None, or a set of handles to include. + if it is None, then there is no evidence to only include + particular handles. If it is a set, then those in the set + are a superset of the items that will match. + + `handles_out` is a set. If any handle is in the set, it will + not be included in the final results. + + The handles_in are selected if all of the rules are connected with + "and" and not inverted. The handles_out are selected if all of the + rules are connected with "and" and inverted. + """ + handles_in = None + handles_out = set() + # Get all positive non-inverted selected_handles + for inverted, logical_op, selected_handles in self.all_selected_handles: + if logical_op == "and" and not inverted: + LOG.debug("optimizer positive match!") + if handles_in is None: + handles_in = intersection(selected_handles) + else: + handles_in = intersection([handles_in] + selected_handles) + + # Get all inverted selected_handles: + for inverted, logical_op, selected_handles in self.all_selected_handles: + if logical_op == "and" and inverted: + LOG.debug("optimizer inverted match!") + handles_out = union([handles_out] + selected_handles) + + if handles_in is not None: + handles_in = handles_in - handles_out + + LOG.debug("optimizer handles_in: %s", len(handles_in) if handles_in else 0) + return handles_in, handles_out diff --git a/gramps/gen/filters/rules/_changedsincebase.py b/gramps/gen/filters/rules/_changedsincebase.py index 3cc57b1ed92..99f220d1bf7 100644 --- a/gramps/gen/filters/rules/_changedsincebase.py +++ b/gramps/gen/filters/rules/_changedsincebase.py @@ -40,6 +40,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.primaryobj import PrimaryObject +from ...db import Database + + # ------------------------------------------------------------------------- # # ChangedSince @@ -93,7 +102,7 @@ def time_str_to_sec(self, time_str): ) return time_sec - def prepare(self, db, user): + def prepare(self, db: Database, user): self.since = None self.before = None if self.list[0]: @@ -101,8 +110,8 @@ def prepare(self, db, user): if self.list[1]: self.before = self.time_str_to_sec(self.list[1]) - def apply(self, db, obj): - obj_time = obj.get_change_time() + def apply_to_one(self, db: Database, obj: PrimaryObject) -> bool: + obj_time = obj.change if self.since: if obj_time < self.since: return False diff --git a/gramps/gen/filters/rules/_everything.py b/gramps/gen/filters/rules/_everything.py index 3ec66484367..303a305f7cb 100644 --- a/gramps/gen/filters/rules/_everything.py +++ b/gramps/gen/filters/rules/_everything.py @@ -35,6 +35,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.primaryobj import PrimaryObject +from ...db import Database + + # ------------------------------------------------------------------------- # # Everyone @@ -50,5 +59,5 @@ class Everything(Rule): def is_empty(self): return True - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: PrimaryObject) -> bool: return True diff --git a/gramps/gen/filters/rules/_hasattributebase.py b/gramps/gen/filters/rules/_hasattributebase.py index 1b29aa2530d..2155e4c4f68 100644 --- a/gramps/gen/filters/rules/_hasattributebase.py +++ b/gramps/gen/filters/rules/_hasattributebase.py @@ -35,6 +35,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.attrbase import AttributeBase +from ...db import Database + + # ------------------------------------------------------------------------- # # HasAttributeBase @@ -55,7 +64,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.attribute_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things that should only be done once. """ @@ -63,14 +72,14 @@ def prepare(self, db, user): self.attribute_type = AttributeType() self.attribute_type.set_from_xml_str(self.list[0]) - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: AttributeBase) -> bool: """ Apply the rule. Return True if a match. """ if self.attribute_type: - for attribute in obj.get_attribute_list(): - name_match = attribute.get_type() == self.attribute_type + for attribute in obj.attribute_list: + name_match = attribute.type == self.attribute_type if name_match: - if self.match_substring(1, attribute.get_value()): + if self.match_substring(1, attribute.value): return True return False diff --git a/gramps/gen/filters/rules/_hascitationbase.py b/gramps/gen/filters/rules/_hascitationbase.py index 0d2c0cfcff7..8292807feca 100644 --- a/gramps/gen/filters/rules/_hascitationbase.py +++ b/gramps/gen/filters/rules/_hascitationbase.py @@ -37,6 +37,16 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.citationbase import CitationBase +from ...lib import Citation +from ...db import Database + + # ------------------------------------------------------------------------- # # HasCitation @@ -62,23 +72,23 @@ def prepare(self, db, user): except: pass - def apply(self, dbase, object): - for citation_handle in object.get_citation_list(): + def apply_to_one(self, dbase: Database, object: CitationBase) -> bool: + for citation_handle in object.citation_list: citation = dbase.get_citation_from_handle(citation_handle) if self._apply(dbase, citation): return True return False - def _apply(self, db, citation): - if not self.match_substring(0, citation.get_page()): + def _apply(self, db: Database, citation: Citation): + if not self.match_substring(0, citation.page): return False if self.date: - if not citation.get_date_object().match(self.date): + if not citation.date.match(self.date): return False if self.list[2]: - if citation.get_confidence_level() < int(self.list[2]): + if citation.confidence < int(self.list[2]): return False return True diff --git a/gramps/gen/filters/rules/_haseventbase.py b/gramps/gen/filters/rules/_haseventbase.py index dff30f83cba..0eff2f512a3 100644 --- a/gramps/gen/filters/rules/_haseventbase.py +++ b/gramps/gen/filters/rules/_haseventbase.py @@ -37,6 +37,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib import Event +from ...db import Database + + # ------------------------------------------------------------------------- # # HasEventBase @@ -58,7 +67,7 @@ def __init__(self, arg, use_regex=False, use_case=False): self.date = None self.event_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things that should only be done once. """ @@ -71,7 +80,7 @@ def prepare(self, db, user): except: pass - def apply(self, db, event): + def apply_to_one(self, db: Database, event: Event) -> bool: """ Apply the rule. Return True if a match. """ @@ -82,15 +91,15 @@ def apply(self, db, event): elif event.type != self.event_type: return False - if not self.match_substring(3, event.get_description()): + if not self.match_substring(3, event.description): return False if self.date: - if not event.get_date_object().match(self.date): + if not event.date.match(self.date): return False if self.list[2]: - place_id = event.get_place_handle() + place_id = event.place if place_id: place = db.get_place_from_handle(place_id) place_title = place_displayer.display(db, place) @@ -100,7 +109,7 @@ def apply(self, db, event): return False if not self.match_substring( - 4, get_participant_from_event(db, event.get_handle(), all_=True) + 4, get_participant_from_event(db, event.handle, all_=True) ): return False diff --git a/gramps/gen/filters/rules/_hasgallerybase.py b/gramps/gen/filters/rules/_hasgallerybase.py index 5bf5c61bee2..342648a2389 100644 --- a/gramps/gen/filters/rules/_hasgallerybase.py +++ b/gramps/gen/filters/rules/_hasgallerybase.py @@ -39,7 +39,16 @@ # ------------------------------------------------------------------------- -# "People who have images" +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.mediabase import MediaBase +from ...db import Database + + +# ------------------------------------------------------------------------- +# "Objects that have images" # ------------------------------------------------------------------------- class HasGalleryBase(Rule): """Objects who have Media Object""" @@ -49,7 +58,7 @@ class HasGalleryBase(Rule): description = "Matches objects with certain number of items in the gallery" category = _("General filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): # things we want to do just once, not for every handle if self.list[1] == "less than": self.count_type = 0 @@ -60,8 +69,8 @@ def prepare(self, db, user): self.userSelectedCount = int(self.list[0]) - def apply(self, db, obj): - count = len(obj.get_media_list()) + def apply_to_one(self, db: Database, obj: MediaBase) -> bool: + count = len(obj.media_list) if self.count_type == 0: # "less than" return count < self.userSelectedCount elif self.count_type == 2: # "greater than" diff --git a/gramps/gen/filters/rules/_hasgrampsid.py b/gramps/gen/filters/rules/_hasgrampsid.py index fbfb5a8262a..cbe90a9bae3 100644 --- a/gramps/gen/filters/rules/_hasgrampsid.py +++ b/gramps/gen/filters/rules/_hasgrampsid.py @@ -36,6 +36,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.primaryobj import PrimaryObject +from ...db import Database + + # ------------------------------------------------------------------------- # # HasIdOf @@ -49,7 +58,7 @@ class HasGrampsId(Rule): description = "Matches objects with a specified Gramps ID" category = _("General filters") - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: PrimaryObject) -> bool: """ apply the rule on the obj. return true if the rule passes, false otherwise. diff --git a/gramps/gen/filters/rules/_hasldsbase.py b/gramps/gen/filters/rules/_hasldsbase.py index 8e29ed2c4fa..015f68e368e 100644 --- a/gramps/gen/filters/rules/_hasldsbase.py +++ b/gramps/gen/filters/rules/_hasldsbase.py @@ -39,6 +39,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.ldsordbase import LdsOrdBase +from ...db import Database + + # ------------------------------------------------------------------------- # # HasLDSBase @@ -52,7 +61,7 @@ class HasLDSBase(Rule): description = "Matches objects with LDS events" category = _("General filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): # things we want to do just once, not for every handle if self.list[1] == "less than": self.count_type = 0 @@ -63,8 +72,8 @@ def prepare(self, db, user): self.userSelectedCount = int(self.list[0]) - def apply(self, db, obj): - count = len(obj.get_lds_ord_list()) + def apply_to_one(self, db: Database, obj: LdsOrdBase) -> bool: + count = len(obj.lds_ord_list) if self.count_type == 0: # "less than" return count < self.userSelectedCount elif self.count_type == 2: # "greater than" diff --git a/gramps/gen/filters/rules/_hasnotebase.py b/gramps/gen/filters/rules/_hasnotebase.py index 441bc582e0a..9b508fb1236 100644 --- a/gramps/gen/filters/rules/_hasnotebase.py +++ b/gramps/gen/filters/rules/_hasnotebase.py @@ -39,6 +39,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.notebase import NoteBase +from ...db import Database + + # ------------------------------------------------------------------------- # "Objects having notes" # ------------------------------------------------------------------------- @@ -58,7 +67,7 @@ def __init__(self, arg, use_regex=False, use_case=False): else: Rule.__init__(self, arg, use_regex, use_case) - def prepare(self, db, user): + def prepare(self, db: Database, user): # things we want to do just once, not for every handle if self.list[1] == "less than": self.count_type = 0 @@ -69,8 +78,8 @@ def prepare(self, db, user): self.userSelectedCount = int(self.list[0]) - def apply(self, db, obj): - count = len(obj.get_note_list()) + def apply_to_one(self, db: Database, obj: NoteBase) -> bool: + count = len(obj.note_list) if self.count_type == 0: # "less than" return count < self.userSelectedCount elif self.count_type == 2: # "greater than" diff --git a/gramps/gen/filters/rules/_hasnoteregexbase.py b/gramps/gen/filters/rules/_hasnoteregexbase.py index 50677efd96a..178771dc811 100644 --- a/gramps/gen/filters/rules/_hasnoteregexbase.py +++ b/gramps/gen/filters/rules/_hasnoteregexbase.py @@ -36,6 +36,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.notebase import NoteBase +from ...db import Database + + # ------------------------------------------------------------------------- # Objects having notes that contain a substring or match a regular expression # ------------------------------------------------------------------------- @@ -51,9 +60,9 @@ class HasNoteRegexBase(Rule): category = _("General filters") allow_regex = True - def apply(self, db, person): - for handle in person.get_note_list(): + def apply_to_one(self, db: Database, obj: NoteBase) -> bool: + for handle in obj.note_list: note = db.get_note_from_handle(handle) - if self.match_substring(0, note.get()): + if self.match_substring(0, str(note.text)): return True return False diff --git a/gramps/gen/filters/rules/_hasnotesubstrbase.py b/gramps/gen/filters/rules/_hasnotesubstrbase.py index 25b3c3fc878..0416e40844e 100644 --- a/gramps/gen/filters/rules/_hasnotesubstrbase.py +++ b/gramps/gen/filters/rules/_hasnotesubstrbase.py @@ -35,6 +35,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib import Person +from ...db import Database + + # ------------------------------------------------------------------------- # "People having notes that contain a substring" # ------------------------------------------------------------------------- @@ -46,11 +55,11 @@ class HasNoteSubstrBase(Rule): description = "Matches objects whose notes contain text matching a " "substring" category = _("General filters") - def apply(self, db, person): - notelist = person.get_note_list() + def apply_to_one(self, db, person: Person) -> bool: + notelist = person.note_list for notehandle in notelist: note = db.get_note_from_handle(notehandle) - n = note.get() + n = str(note.text) if n.upper().find(self.list[0].upper()) != -1: return True return False diff --git a/gramps/gen/filters/rules/_hasnotetypebase.py b/gramps/gen/filters/rules/_hasnotetypebase.py index 464cf141f98..5b93ef4cc5b 100644 --- a/gramps/gen/filters/rules/_hasnotetypebase.py +++ b/gramps/gen/filters/rules/_hasnotetypebase.py @@ -31,6 +31,14 @@ from ...lib.notetype import NoteType from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.notebase import NoteBase +from ...db import Database + _ = glocale.translation.gettext @@ -53,7 +61,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.note_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -61,11 +69,11 @@ def prepare(self, db, user): self.note_type = NoteType() self.note_type.set_from_xml_str(self.list[0]) - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: NoteBase): """ Apply the rule. Return True on a match. """ - notelist = obj.get_note_list() + notelist = obj.note_list for notehandle in notelist: note = db.get_note_from_handle(notehandle) if note.get_type() == self.note_type: diff --git a/gramps/gen/filters/rules/_hasreferencecountbase.py b/gramps/gen/filters/rules/_hasreferencecountbase.py index c8a6e2977db..b99a86ec6d5 100644 --- a/gramps/gen/filters/rules/_hasreferencecountbase.py +++ b/gramps/gen/filters/rules/_hasreferencecountbase.py @@ -35,6 +35,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.primaryobj import PrimaryObject +from ...db import Database + + # ------------------------------------------------------------------------- # "Objects with a certain reference count" # ------------------------------------------------------------------------- @@ -46,7 +55,7 @@ class HasReferenceCountBase(Rule): description = "Matches objects with a certain reference count" category = _("General filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): # things we want to do just once, not for every handle if self.list[0] == "less than": self.count_type = 0 @@ -57,8 +66,8 @@ def prepare(self, db, user): self.userSelectedCount = int(self.list[1]) - def apply(self, db, obj): - handle = obj.get_handle() + def apply_to_one(self, db: Database, obj: PrimaryObject) -> bool: + handle = obj.handle count = 0 for item in db.find_backlink_handles(handle): count += 1 diff --git a/gramps/gen/filters/rules/_hassourcebase.py b/gramps/gen/filters/rules/_hassourcebase.py index 024143571ae..25db1b4a5c0 100644 --- a/gramps/gen/filters/rules/_hassourcebase.py +++ b/gramps/gen/filters/rules/_hassourcebase.py @@ -36,6 +36,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Any +from ...db import Database + + # ------------------------------------------------------------------------- # # HasSource @@ -50,17 +59,17 @@ class HasSourceBase(Rule): category = _("Citation/source filters") allow_regex = True - def apply(self, db, source): - if not self.match_substring(0, source.get_title()): + def apply_to_one(self, db: Database, source: Any) -> bool: + if not self.match_substring(0, source.title): return False - if not self.match_substring(1, source.get_author()): + if not self.match_substring(1, source.author): return False - if not self.match_substring(2, source.get_abbreviation()): + if not self.match_substring(2, source.abbrev): return False - if not self.match_substring(3, source.get_publication_info()): + if not self.match_substring(3, source.pubinfo): return False return True diff --git a/gramps/gen/filters/rules/_hassourcecountbase.py b/gramps/gen/filters/rules/_hassourcecountbase.py index 8b4aab709bc..2df9ef2ca51 100644 --- a/gramps/gen/filters/rules/_hassourcecountbase.py +++ b/gramps/gen/filters/rules/_hassourcecountbase.py @@ -38,6 +38,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.citationbase import CitationBase +from ...db import Database + + # ------------------------------------------------------------------------- # "Objects having sources" # ------------------------------------------------------------------------- @@ -52,7 +61,7 @@ class HasSourceCountBase(Rule): ) category = _("Citation/source filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): # things we want to do just once, not for every handle if self.list[1] == "less than": self.count_type = 0 @@ -63,8 +72,8 @@ def prepare(self, db, user): self.userSelectedCount = int(self.list[0]) - def apply(self, db, obj): - count = len(obj.get_citation_list()) + def apply_to_one(self, db: Database, obj: CitationBase) -> bool: + count = len(obj.citation_list) if self.count_type == 0: # "less than" return count < self.userSelectedCount elif self.count_type == 2: # "greater than" diff --git a/gramps/gen/filters/rules/_hassourceofbase.py b/gramps/gen/filters/rules/_hassourceofbase.py index 4e04be727fa..0786011be3d 100644 --- a/gramps/gen/filters/rules/_hassourceofbase.py +++ b/gramps/gen/filters/rules/_hassourceofbase.py @@ -36,6 +36,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.citationbase import CitationBase +from ...db import Database + + # ------------------------------------------------------------------------- # # HasSourceOf @@ -49,7 +58,7 @@ class HasSourceOfBase(Rule): category = _("Citation/source filters") description = "Matches objects who have a particular source" - def prepare(self, db, user): + def prepare(self, db: Database, user): if self.list[0] == "": self.source_handle = None self.nosource = True @@ -57,11 +66,11 @@ def prepare(self, db, user): self.nosource = False try: - self.source_handle = db.get_source_from_gramps_id(self.list[0]).get_handle() + self.source_handle = db.get_source_from_gramps_id(self.list[0]).handle except: self.source_handle = None - def apply(self, db, object): + def apply_to_one(self, db: Database, object: CitationBase) -> bool: if not self.source_handle: if self.nosource: # check whether the citation list is empty as a proxy for @@ -72,6 +81,6 @@ def apply(self, db, object): else: for citation_handle in object.get_all_citation_lists(): citation = db.get_citation_from_handle(citation_handle) - if citation.get_reference_handle() == self.source_handle: + if citation.source_handle == self.source_handle: return True return False diff --git a/gramps/gen/filters/rules/_hastagbase.py b/gramps/gen/filters/rules/_hastagbase.py index 9d708776121..04564e32cac 100644 --- a/gramps/gen/filters/rules/_hastagbase.py +++ b/gramps/gen/filters/rules/_hastagbase.py @@ -38,6 +38,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.primaryobj import PrimaryObject +from ...db import Database + + # ------------------------------------------------------------------------- # # HasTag @@ -53,19 +62,19 @@ class HasTagBase(Rule): description = "Matches objects with the given tag" category = _("General filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we want to do just once. """ self.tag_handle = None tag = db.get_tag_from_name(self.list[0]) if tag is not None: - self.tag_handle = tag.get_handle() + self.tag_handle = tag.handle - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: PrimaryObject) -> bool: """ Apply the rule. Return True for a match. """ if self.tag_handle is None: return False - return self.tag_handle in obj.get_tag_list() + return self.tag_handle in obj.tag_list diff --git a/gramps/gen/filters/rules/_isprivate.py b/gramps/gen/filters/rules/_isprivate.py index 53c2b95a0fa..2f6179ef059 100644 --- a/gramps/gen/filters/rules/_isprivate.py +++ b/gramps/gen/filters/rules/_isprivate.py @@ -33,6 +33,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.primaryobj import PrimaryObject +from ...db import Database + + # ------------------------------------------------------------------------- # "People marked private" # ------------------------------------------------------------------------- @@ -43,5 +52,5 @@ class IsPrivate(Rule): description = "Matches objects that are indicated as private" category = _("General filters") - def apply(self, db, obj): - return obj.get_privacy() + def apply_to_one(self, db: Database, obj: PrimaryObject) -> bool: + return obj.private diff --git a/gramps/gen/filters/rules/_ispublic.py b/gramps/gen/filters/rules/_ispublic.py index 2ffdf608120..a765f3140fd 100644 --- a/gramps/gen/filters/rules/_ispublic.py +++ b/gramps/gen/filters/rules/_ispublic.py @@ -30,6 +30,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.primaryobj import PrimaryObject +from ...db import Database + + # ------------------------------------------------------------------------- # "People marked public" # ------------------------------------------------------------------------- @@ -40,5 +49,5 @@ class IsPublic(Rule): description = "Matches objects that are not indicated as private" category = _("General filters") - def apply(self, db, obj): - return not obj.get_privacy() + def apply_to_one(self, db: Database, obj: PrimaryObject) -> bool: + return not obj.private diff --git a/gramps/gen/filters/rules/_matcheseventfilterbase.py b/gramps/gen/filters/rules/_matcheseventfilterbase.py index 9e551392f39..42b4ffc4c0a 100644 --- a/gramps/gen/filters/rules/_matcheseventfilterbase.py +++ b/gramps/gen/filters/rules/_matcheseventfilterbase.py @@ -35,6 +35,15 @@ from . import MatchesFilterBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.eventbase import EventBase +from ...db import Database + + # ------------------------------------------------------------------------- # # MatchesEventFilter @@ -57,17 +66,18 @@ class MatchesEventFilterBase(MatchesFilterBase): # we want to have this filter show event filters namespace = "Event" - def prepare(self, db, user): + def prepare(self, db: Database, user): MatchesFilterBase.prepare(self, db, user) self.MEF_filt = self.find_filter() - def apply(self, db, object): + def apply_to_one(self, db: Database, object: EventBase) -> bool: if self.MEF_filt is None: return False - eventlist = [x.ref for x in object.get_event_ref_list()] + eventlist = [x.ref for x in object.event_ref_list] for eventhandle in eventlist: # check if event in event filter - if self.MEF_filt.check(db, eventhandle): + event = db.get_event_from_handle(eventhandle) + if self.MEF_filt.apply_to_one(db, event): return True return False diff --git a/gramps/gen/filters/rules/_matchesfilterbase.py b/gramps/gen/filters/rules/_matchesfilterbase.py index 208b876e75c..04afca52785 100644 --- a/gramps/gen/filters/rules/_matchesfilterbase.py +++ b/gramps/gen/filters/rules/_matchesfilterbase.py @@ -41,6 +41,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Any, Union +from ...db import Database + + # ------------------------------------------------------------------------- # # MatchesFilter @@ -59,8 +68,9 @@ class MatchesFilterBase(Rule): name = "Objects matching the " description = "Matches objects matched by the specified filter name" category = _("General filters") + namespace: Union[str, None] = None - def prepare(self, db, user): + def prepare(self, db: Database, user): if gramps.gen.filters.CustomFilters: filters = gramps.gen.filters.CustomFilters.get_filters_dict(self.namespace) if self.list[0] in filters: @@ -85,12 +95,12 @@ def reset(self): for rule in filt.flist: rule.requestreset() - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: Any) -> bool: if gramps.gen.filters.CustomFilters: filters = gramps.gen.filters.CustomFilters.get_filters_dict(self.namespace) if self.list[0] in filters: filt = filters[self.list[0]] - return filt.check(db, obj.handle) + return filt.apply_to_one(db, obj) return False def find_filter(self): diff --git a/gramps/gen/filters/rules/_matchessourceconfidencebase.py b/gramps/gen/filters/rules/_matchessourceconfidencebase.py index ccb9e8eb059..285e89be8eb 100644 --- a/gramps/gen/filters/rules/_matchessourceconfidencebase.py +++ b/gramps/gen/filters/rules/_matchessourceconfidencebase.py @@ -39,6 +39,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.citationbase import CitationBase +from ...db import Database + + # ------------------------------------------------------------------------- # "Confidence level" # Sources of an attribute of an event are ignored @@ -53,10 +62,10 @@ class MatchesSourceConfidenceBase(Rule): ) category = _("Citation/source filters") - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: CitationBase) -> bool: required_conf = int(self.list[0]) - for citation_handle in obj.get_citation_list(): + for citation_handle in obj.citation_list: citation = db.get_citation_from_handle(citation_handle) - if required_conf <= citation.get_confidence_level(): + if required_conf <= citation.confidence: return True return False diff --git a/gramps/gen/filters/rules/_matchessourcefilterbase.py b/gramps/gen/filters/rules/_matchessourcefilterbase.py index 1f9e9801749..8269c7766fb 100644 --- a/gramps/gen/filters/rules/_matchessourcefilterbase.py +++ b/gramps/gen/filters/rules/_matchessourcefilterbase.py @@ -36,6 +36,15 @@ from . import MatchesFilterBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.citationbase import CitationBase +from ...db import Database + + # ------------------------------------------------------------------------- # # MatchesFilter @@ -56,17 +65,17 @@ class MatchesSourceFilterBase(MatchesFilterBase): # we want to have this filter show source filters namespace = "Source" - def prepare(self, db, user): + def prepare(self, db: Database, user): MatchesFilterBase.prepare(self, db, user) self.MSF_filt = self.find_filter() - def apply(self, db, object): + def apply_to_one(self, db, object: CitationBase) -> bool: if self.MSF_filt is None: return False - for citation_handle in object.get_citation_list(): + for citation_handle in object.citation_list: citation = db.get_citation_from_handle(citation_handle) - sourcehandle = citation.get_reference_handle() - if self.MSF_filt.check(db, sourcehandle): + source = db.get_source_from_handle(citation.ref) + if self.MSF_filt.apply_to_one(db, source): return True return False diff --git a/gramps/gen/filters/rules/_regexpidbase.py b/gramps/gen/filters/rules/_regexpidbase.py index 14b5c9a687f..f469c1c4faa 100644 --- a/gramps/gen/filters/rules/_regexpidbase.py +++ b/gramps/gen/filters/rules/_regexpidbase.py @@ -36,6 +36,15 @@ from . import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ...lib.primaryobj import PrimaryObject +from ...db import Database + + # ------------------------------------------------------------------------- # # HasIdOf @@ -56,5 +65,5 @@ class RegExpIdBase(Rule): category = _("General filters") allow_regex = True - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: PrimaryObject) -> bool: return self.match_substring(0, obj.gramps_id) diff --git a/gramps/gen/filters/rules/_rule.py b/gramps/gen/filters/rules/_rule.py index d8227380a01..deb8bd5da49 100644 --- a/gramps/gen/filters/rules/_rule.py +++ b/gramps/gen/filters/rules/_rule.py @@ -46,6 +46,15 @@ LOG = logging.getLogger(".") +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Any, List +from ...db import Database + + # ------------------------------------------------------------------------- # # Rule @@ -72,7 +81,7 @@ def __init__(self, arg, use_regex=False, use_case=False): def is_empty(self): return False - def requestprepare(self, db, user): + def requestprepare(self, db: Database, user): """ Request that the prepare method of the rule is executed if needed @@ -107,7 +116,7 @@ def requestprepare(self, db, user): ), ) - def prepare(self, db, user): + def prepare(self, db: Database, user): """prepare so the rule can be executed efficiently""" pass @@ -151,7 +160,7 @@ def check(self): """Verify the number of rule values versus the number of rule labels.""" return len(self.list) == len(self.labels) - def apply(self, dummy_db, dummy_person): + def apply(self, dummy_db: Database, dummy_person: Any) -> bool: """Apply the rule to some database entry; must be overwritten.""" return True diff --git a/gramps/gen/filters/rules/citation/_hascitation.py b/gramps/gen/filters/rules/citation/_hascitation.py index af08fc5a40c..e3cfe3049ea 100644 --- a/gramps/gen/filters/rules/citation/_hascitation.py +++ b/gramps/gen/filters/rules/citation/_hascitation.py @@ -39,6 +39,15 @@ from ....datehandler import parser +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Citation +from ....db import Database + + # ------------------------------------------------------------------------- # # HasCitation @@ -53,7 +62,7 @@ class HasCitation(Rule): description = _("Matches citations with particular parameters") allow_regex = True - def prepare(self, db, user): + def prepare(self, db: Database, user): self.date = None try: if self.list[1]: @@ -61,16 +70,16 @@ def prepare(self, db, user): except: pass - def apply(self, dbase, citation): - if not self.match_substring(0, citation.get_page()): + def apply_to_one(self, dbase: Database, citation: Citation) -> bool: + if not self.match_substring(0, citation.page): return False if self.date: - if not citation.get_date_object().match(self.date): + if not citation.date.match(self.date): return False if self.list[2]: - if citation.get_confidence_level() < int(self.list[2]): + if citation.confidence < int(self.list[2]): return False return True diff --git a/gramps/gen/filters/rules/citation/_hassource.py b/gramps/gen/filters/rules/citation/_hassource.py index 1a63bfcaaf9..ced9765e103 100644 --- a/gramps/gen/filters/rules/citation/_hassource.py +++ b/gramps/gen/filters/rules/citation/_hassource.py @@ -39,6 +39,15 @@ from .._hassourcebase import HasSourceBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Any +from ....db import Database + + # ------------------------------------------------------------------------- # # HasEvent @@ -52,8 +61,6 @@ class HasSource(HasSourceBase): description = _("Matches citations with a source of a particular " "value") category = _("Source filters") - def apply(self, dbase, citation): - source = dbase.get_source_from_handle(citation.get_reference_handle()) - if HasSourceBase.apply(self, dbase, source): - return True - return False + def apply_to_one(self, dbase: Database, citation: Any) -> bool: + source = dbase.get_source_from_handle(citation.ref) + return HasSourceBase.apply_to_one(self, dbase, source) diff --git a/gramps/gen/filters/rules/citation/_hassourceidof.py b/gramps/gen/filters/rules/citation/_hassourceidof.py index de6e07b765e..d4a2ba56503 100644 --- a/gramps/gen/filters/rules/citation/_hassourceidof.py +++ b/gramps/gen/filters/rules/citation/_hassourceidof.py @@ -36,6 +36,15 @@ from .._hasgrampsid import HasGrampsId +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib.refbase import RefBase +from ....db import Database + + # ------------------------------------------------------------------------- # # HasSourceIdOf @@ -49,8 +58,6 @@ class HasSourceIdOf(HasGrampsId): description = _("Matches a citation with a source with a specified Gramps " "ID") category = _("Source filters") - def apply(self, dbase, citation): - source = dbase.get_source_from_handle(citation.get_reference_handle()) - if HasGrampsId.apply(self, dbase, source): - return True - return False + def apply_to_one(self, dbase: Database, citation: RefBase) -> bool: # type: ignore[override] + source = dbase.get_source_from_handle(citation.ref) + return HasGrampsId.apply_to_one(self, dbase, source) # type: ignore[override] diff --git a/gramps/gen/filters/rules/citation/_hassourcenoteregexp.py b/gramps/gen/filters/rules/citation/_hassourcenoteregexp.py index 82ceeb2d4e1..7b5fae283fc 100644 --- a/gramps/gen/filters/rules/citation/_hassourcenoteregexp.py +++ b/gramps/gen/filters/rules/citation/_hassourcenoteregexp.py @@ -40,6 +40,15 @@ from .._hasnoteregexbase import HasNoteRegexBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib.refbase import RefBase +from ....db import Database + + # ------------------------------------------------------------------------- # # HasSourceNoteRegexp @@ -58,8 +67,6 @@ class HasSourceNoteRegexp(HasNoteRegexBase): ) category = _("Source filters") - def apply(self, db, citation): - source = db.get_source_from_handle(citation.get_reference_handle()) - if HasNoteRegexBase.apply(self, db, source): - return True - return False + def apply_to_one(self, db: Database, citation: RefBase) -> bool: # type: ignore[override] + source = db.get_source_from_handle(citation.ref) + return HasNoteRegexBase.apply_to_one(self, db, source) diff --git a/gramps/gen/filters/rules/citation/_matchespagesubstringof.py b/gramps/gen/filters/rules/citation/_matchespagesubstringof.py index f45abf4650c..a05463882bc 100644 --- a/gramps/gen/filters/rules/citation/_matchespagesubstringof.py +++ b/gramps/gen/filters/rules/citation/_matchespagesubstringof.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Citation +from ....db import Database + + # ------------------------------------------------------------------------- # "Sources having a title that contain a substring" # ------------------------------------------------------------------------- @@ -49,6 +58,6 @@ class MatchesPageSubstringOf(Rule): category = _("General filters") allow_regex = True - def apply(self, db, object): + def apply_to_one(self, db: Database, object: Citation) -> bool: """Apply the filter""" - return self.match_substring(0, object.get_page()) + return self.match_substring(0, object.page) diff --git a/gramps/gen/filters/rules/citation/_matchesrepositoryfilter.py b/gramps/gen/filters/rules/citation/_matchesrepositoryfilter.py index a9260647d48..dfcd33659b2 100644 --- a/gramps/gen/filters/rules/citation/_matchesrepositoryfilter.py +++ b/gramps/gen/filters/rules/citation/_matchesrepositoryfilter.py @@ -39,6 +39,15 @@ from .. import MatchesFilterBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Citation +from ....db import Database + + # ------------------------------------------------------------------------- # "Sources which reference a repository by selection" # ------------------------------------------------------------------------- @@ -59,19 +68,20 @@ class MatchesRepositoryFilter(MatchesFilterBase): # we want to have this filter show repository filters namespace = "Repository" - def prepare(self, db, user): + def prepare(self, db: Database, user): MatchesFilterBase.prepare(self, db, user) self.MRF_filt = self.find_filter() - def apply(self, db, object): + def apply_to_one(self, db: Database, object: Citation) -> bool: if self.MRF_filt is None: return False source_handle = object.source_handle source = db.get_source_from_handle(source_handle) - repolist = [x.ref for x in source.get_reporef_list()] + repolist = [x.ref for x in source.reporef_list] for repohandle in repolist: # check if repo in repository filter - if self.MRF_filt.check(db, repohandle): + repo = db.get_repository_from_handle(repohandle) + if self.MRF_filt.apply_to_one(db, repo): return True return False diff --git a/gramps/gen/filters/rules/citation/_matchessourcefilter.py b/gramps/gen/filters/rules/citation/_matchessourcefilter.py index 0bc9c18fc2c..28cd61297ba 100644 --- a/gramps/gen/filters/rules/citation/_matchessourcefilter.py +++ b/gramps/gen/filters/rules/citation/_matchessourcefilter.py @@ -37,6 +37,15 @@ from .. import MatchesFilterBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Citation +from ....db import Database + + # ------------------------------------------------------------------------- # # MatchesFilter @@ -57,15 +66,14 @@ class MatchesSourceFilter(MatchesFilterBase): # we want to have this filter show source filters namespace = "Source" - def prepare(self, db, user): + def prepare(self, db: Database, user): MatchesFilterBase.prepare(self, db, user) self.MRF_filt = self.find_filter() - def apply(self, db, object): + def apply_to_one(self, db: Database, object: Citation) -> bool: if self.MRF_filt is None: return False source_handle = object.source_handle - if self.MRF_filt.check(db, source_handle): - return True - return False + source = db.get_source_from_handle(source_handle) + return self.MRF_filt.apply_to_one(db, source) diff --git a/gramps/gen/filters/rules/citation/_regexpsourceidof.py b/gramps/gen/filters/rules/citation/_regexpsourceidof.py index afa7f2471aa..2c9dedcc6d4 100644 --- a/gramps/gen/filters/rules/citation/_regexpsourceidof.py +++ b/gramps/gen/filters/rules/citation/_regexpsourceidof.py @@ -36,6 +36,15 @@ from .._regexpidbase import RegExpIdBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib.refbase import RefBase +from ....db import Database + + # ------------------------------------------------------------------------- # # HasIdOf @@ -54,8 +63,6 @@ class RegExpSourceIdOf(RegExpIdBase): ) category = _("Source filters") - def apply(self, dbase, citation): - source = dbase.get_source_from_handle(citation.get_reference_handle()) - if RegExpIdBase.apply(self, dbase, source): - return True - return False + def apply_to_one(self, dbase: Database, citation: RefBase) -> bool: # type: ignore[override] + source = dbase.get_source_from_handle(citation.ref) + return RegExpIdBase.apply_to_one(self, dbase, source) diff --git a/gramps/gen/filters/rules/event/_hasdata.py b/gramps/gen/filters/rules/event/_hasdata.py index 51352930af9..95e3206b35c 100644 --- a/gramps/gen/filters/rules/event/_hasdata.py +++ b/gramps/gen/filters/rules/event/_hasdata.py @@ -36,6 +36,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Event +from ....db import Database + + # ------------------------------------------------------------------------- # # HasData @@ -57,7 +66,7 @@ def __init__(self, arg, use_regex=False, use_case=False): self.event_type = None self.date = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -71,20 +80,20 @@ def prepare(self, db, user): if self.date: self.date = parser.parse(self.date) - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: Event) -> bool: """ Apply the rule. Return True on a match. """ - if self.event_type and obj.get_type() != self.event_type: + if self.event_type and obj.type != self.event_type: # No match return False - if self.date and not obj.get_date_object().match(self.date): + if self.date and not obj.date.match(self.date): # No match return False if self.list[2]: - place_id = obj.get_place_handle() + place_id = obj.place if place_id: place = db.get_place_from_handle(place_id) place_title = place_displayer.display(db, place) @@ -95,7 +104,7 @@ def apply(self, db, obj): # No place attached to event return False - if not self.match_substring(3, obj.get_description()): + if not self.match_substring(3, obj.description): # No match return False diff --git a/gramps/gen/filters/rules/event/_hasdayofweek.py b/gramps/gen/filters/rules/event/_hasdayofweek.py index b4aad3c21e2..0ec04bcb386 100644 --- a/gramps/gen/filters/rules/event/_hasdayofweek.py +++ b/gramps/gen/filters/rules/event/_hasdayofweek.py @@ -29,6 +29,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Event +from ....db import Database + + # ------------------------------------------------------------------------- # # HasDayOfWeek @@ -42,9 +51,11 @@ class HasDayOfWeek(Rule): description = _("Matches events occurring on a particular day of the week") category = _("General filters") - def apply(self, db, event): + def apply_to_one(self, db: Database, event: Event) -> bool: if not self.list[0]: return False else: - dow = event.get_date_object().get_dow() - return dow == int(self.list[0]) + if event.date: + dow = event.date.get_dow() + return dow == int(self.list[0]) + return False diff --git a/gramps/gen/filters/rules/event/_hastype.py b/gramps/gen/filters/rules/event/_hastype.py index bec57745b7a..49d233d437e 100644 --- a/gramps/gen/filters/rules/event/_hastype.py +++ b/gramps/gen/filters/rules/event/_hastype.py @@ -34,6 +34,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Event +from ....db import Database + + # ------------------------------------------------------------------------- # # HasType @@ -53,7 +62,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.event_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -61,10 +70,10 @@ def prepare(self, db, user): self.event_type = EventType() self.event_type.set_from_xml_str(self.list[0]) - def apply(self, _db, obj): + def apply_to_one(self, _db: Database, obj: Event) -> bool: """ Apply the rule. Return True if a match. """ if self.event_type: - return obj.get_type() == self.event_type + return obj.type == self.event_type return False diff --git a/gramps/gen/filters/rules/event/_matchespersonfilter.py b/gramps/gen/filters/rules/event/_matchespersonfilter.py index b82cb22e335..a1985f24d04 100644 --- a/gramps/gen/filters/rules/event/_matchespersonfilter.py +++ b/gramps/gen/filters/rules/event/_matchespersonfilter.py @@ -35,6 +35,15 @@ from .. import MatchesFilterBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Event +from ....db import Database + + # ------------------------------------------------------------------------- # # MatchesFilter @@ -59,7 +68,7 @@ class MatchesPersonFilter(MatchesFilterBase): # we want to have this filter show person filters namespace = "Person" - def prepare(self, db, user): + def prepare(self, db: Database, user): MatchesFilterBase.prepare(self, db, user) try: @@ -70,23 +79,28 @@ def prepare(self, db, user): except IndexError: self.MPF_famevents = False - def apply(self, db, event): + def apply_to_one(self, db: Database, event: Event) -> bool: filt = self.find_filter() if filt: - for classname, handle in db.find_backlink_handles( - event.get_handle(), ["Person"] - ): - if filt.check(db, handle): + for classname, handle in db.find_backlink_handles(event.handle, ["Person"]): + person = db.method("get_%_from_handle", classname)(handle) + if filt.apply_to_one(db, person): return True if self.MPF_famevents: # also include if family event of the person for classname, handle in db.find_backlink_handles( - event.get_handle(), ["Family"] + event.handle, ["Family"] ): family = db.get_family_from_handle(handle) - if family.father_handle and filt.check(db, family.father_handle): + father = ( + db.get_person_from_handle(family.father_handle) + if family + else None + ) + if father and filt.apply_to_one(db, father): return True - if family.mother_handle and filt.check(db, family.mother_handle): + mother = db.get_person_from_handle(family.mother_handle) + if family.mother_handle and filt.apply_to_one(db, mother): return True return False diff --git a/gramps/gen/filters/rules/event/_matchesplacefilter.py b/gramps/gen/filters/rules/event/_matchesplacefilter.py index b05924b0fd8..0e1f0b207a1 100644 --- a/gramps/gen/filters/rules/event/_matchesplacefilter.py +++ b/gramps/gen/filters/rules/event/_matchesplacefilter.py @@ -35,6 +35,15 @@ from .._matchesfilterbase import MatchesFilterBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Event +from ....db import Database + + # ------------------------------------------------------------------------- # # MatchesFilter @@ -58,10 +67,9 @@ class MatchesPlaceFilter(MatchesFilterBase): # we want to have this filter show place filters namespace = "Place" - def apply(self, db, event): + def apply_to_one(self, db: Database, event: Event) -> bool: filt = self.find_filter() if filt: - handle = event.get_place_handle() - if handle and filt.check(db, handle): + if event and filt.apply_to_one(db, event): return True return False diff --git a/gramps/gen/filters/rules/family/_childhasidof.py b/gramps/gen/filters/rules/family/_childhasidof.py index ab1b2069244..b4bc0a13322 100644 --- a/gramps/gen/filters/rules/family/_childhasidof.py +++ b/gramps/gen/filters/rules/family/_childhasidof.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from .. import RegExpIdBase -from ._memberbase import child_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -48,5 +56,10 @@ class ChildHasIdOf(RegExpIdBase): name = _("Families having child with Id containing ") description = _("Matches families where child has a specified " "Gramps ID") category = _("Child filters") - base_class = RegExpIdBase - apply = child_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + for child_ref in family.child_ref_list: + child = db.get_person_from_handle(child_ref.ref) + if super().apply_to_one(db, child): + return True + return False diff --git a/gramps/gen/filters/rules/family/_childhasnameof.py b/gramps/gen/filters/rules/family/_childhasnameof.py index 0da60f54884..b7311b4c1db 100644 --- a/gramps/gen/filters/rules/family/_childhasnameof.py +++ b/gramps/gen/filters/rules/family/_childhasnameof.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from ..person import HasNameOf -from ._memberbase import child_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -41,11 +49,18 @@ # HasNameOf # # ------------------------------------------------------------------------- + + class ChildHasNameOf(HasNameOf): """Rule that checks for full or partial name matches""" name = _("Families with child with the ") description = _("Matches families where child has a specified " "(partial) name") category = _("Child filters") - base_class = HasNameOf - apply = child_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + for child_ref in family.child_ref_list: + child = db.get_person_from_handle(child_ref.ref) + if super().apply_to_one(db, child): + return True + return False diff --git a/gramps/gen/filters/rules/family/_fatherhasidof.py b/gramps/gen/filters/rules/family/_fatherhasidof.py index 5b8ccf79bbf..16d60b57d38 100644 --- a/gramps/gen/filters/rules/family/_fatherhasidof.py +++ b/gramps/gen/filters/rules/family/_fatherhasidof.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from .. import RegExpIdBase -from ._memberbase import father_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -48,5 +56,11 @@ class FatherHasIdOf(RegExpIdBase): name = _("Families having father with Id containing ") description = _("Matches families whose father has a specified " "Gramps ID") category = _("Father filters") - base_class = RegExpIdBase - apply = father_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + father_handle = family.father_handle + if father_handle: + father = db.get_person_from_handle(father_handle) + if father: + return super().apply_to_one(db, father) + return False diff --git a/gramps/gen/filters/rules/family/_fatherhasnameof.py b/gramps/gen/filters/rules/family/_fatherhasnameof.py index c9030f313f9..be955da9a9c 100644 --- a/gramps/gen/filters/rules/family/_fatherhasnameof.py +++ b/gramps/gen/filters/rules/family/_fatherhasnameof.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from ..person import HasNameOf -from ._memberbase import father_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -47,5 +55,13 @@ class FatherHasNameOf(HasNameOf): name = _("Families with father with the ") description = _("Matches families whose father has a specified " "(partial) name") category = _("Father filters") - base_class = HasNameOf - apply = father_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + father_handle = family.father_handle + if father_handle: + father = db.get_person_from_handle(father_handle) + if father: + return super().apply_to_one(db, father) + else: + return False + return False diff --git a/gramps/gen/filters/rules/family/_hasevent.py b/gramps/gen/filters/rules/family/_hasevent.py index 0662e546707..3cb72c32794 100644 --- a/gramps/gen/filters/rules/family/_hasevent.py +++ b/gramps/gen/filters/rules/family/_hasevent.py @@ -38,6 +38,15 @@ from .._haseventbase import HasEventBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database + + # ------------------------------------------------------------------------- # # HasEvent @@ -56,11 +65,11 @@ class HasEvent(HasEventBase): name = _("Families with the ") description = _("Matches families with an event of a particular value") - def apply(self, dbase, family): - for event_ref in family.get_event_ref_list(): + def apply_to_one(self, dbase: Database, family: Family) -> bool: # type: ignore[override] + for event_ref in family.event_ref_list: if not event_ref: continue event = dbase.get_event_from_handle(event_ref.ref) - if HasEventBase.apply(self, dbase, event): + if HasEventBase.apply_to_one(self, dbase, event): return True return False diff --git a/gramps/gen/filters/rules/family/_hasreltype.py b/gramps/gen/filters/rules/family/_hasreltype.py index 6b59e88b5df..cf269f71f6d 100644 --- a/gramps/gen/filters/rules/family/_hasreltype.py +++ b/gramps/gen/filters/rules/family/_hasreltype.py @@ -34,6 +34,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database + + # ------------------------------------------------------------------------- # # HasRelType @@ -53,7 +62,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.relation_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -61,14 +70,14 @@ def prepare(self, db, user): self.relation_type = FamilyRelType() self.relation_type.set_from_xml_str(self.list[0]) - def apply(self, _db, obj): + def apply_to_one(self, _db: Database, obj: Family) -> bool: """ Apply the rule. Return True on a match. """ if self.relation_type: if self.relation_type.is_custom() and self.use_regex: - if self.regex[0].search(str(obj.get_relationship())) is None: + if self.regex[0].search(str(obj.type)) is None: return False - elif self.relation_type != obj.get_relationship(): + elif self.relation_type != obj.type: return False return True diff --git a/gramps/gen/filters/rules/family/_hastwins.py b/gramps/gen/filters/rules/family/_hastwins.py index fdaa8f19647..8be294d05f7 100644 --- a/gramps/gen/filters/rules/family/_hastwins.py +++ b/gramps/gen/filters/rules/family/_hastwins.py @@ -36,6 +36,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database + + # ------------------------------------------------------------------------- # # HasTwins @@ -48,18 +57,19 @@ class HasTwins(Rule): description = _("Matches families with twins") category = _("Child filters") - def apply(self, db, family): + def apply_to_one(self, db: Database, family: Family) -> bool: date_list = [] - for childref in family.get_child_ref_list(): - if int(childref.get_mother_relation()) == ChildRefType.BIRTH: + for childref in family.child_ref_list: + if int(childref.mrel.value) == ChildRefType.BIRTH: child = db.get_person_from_handle(childref.ref) - birthref = child.get_birth_ref() - if birthref: - birth = db.get_event_from_handle(birthref.ref) - sortval = birth.get_date_object().get_sort_value() - if sortval != 0: - if sortval in date_list: - return True - else: - date_list.append(sortval) + if 0 <= child.birth_ref_index < len(child.event_ref_list): + birthref = child.event_ref_list[child.birth_ref_index] + if birthref: + birth = db.get_event_from_handle(birthref.ref) + sortval = birth.date.sortval + if sortval != 0: + if sortval in date_list: + return True + else: + date_list.append(sortval) return False diff --git a/gramps/gen/filters/rules/family/_isancestorof.py b/gramps/gen/filters/rules/family/_isancestorof.py index f9d9473b7e0..8cc68c0f496 100644 --- a/gramps/gen/filters/rules/family/_isancestorof.py +++ b/gramps/gen/filters/rules/family/_isancestorof.py @@ -31,6 +31,16 @@ from .. import Rule from ....const import GRAMPS_LOCALE as glocale +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Family +from ....db import Database + + _ = glocale.translation.gettext @@ -49,33 +59,37 @@ class IsAncestorOf(Rule): category = _("General filters") description = _("Matches ancestor families of the specified family") - def prepare(self, db, user): - self.map = set() + def prepare(self, db: Database, user): + self.selected_handles: Set[str] = set() first = False if int(self.list[1]) else True root_family = db.get_family_from_gramps_id(self.list[0]) self.init_list(db, root_family, first) def reset(self): - self.map.clear() + self.selected_handles.clear() - def apply(self, db, family): - return family.handle in self.map + def apply_to_one(self, db: Database, family: Family) -> bool: + return family.handle in self.selected_handles - def init_list(self, db, family, first): + def init_list(self, db: Database, family: Family, first: bool): """ Initialise family handle list. """ if not family: return - if family.handle in self.map: + if family.handle in self.selected_handles: return if not first: - self.map.add(family.handle) + self.selected_handles.add(family.handle) - for parent_handle in [family.get_father_handle(), family.get_mother_handle()]: + for parent_handle in [family.father_handle, family.mother_handle]: if parent_handle: parent = db.get_person_from_handle(parent_handle) - family_handle = parent.get_main_parents_family_handle() + family_handle = ( + parent.parent_family_list[0] + if len(parent.parent_family_list) > 0 + else None + ) if family_handle: parent_family = db.get_family_from_handle(family_handle) - self.init_list(db, parent_family, 0) + self.init_list(db, parent_family, False) diff --git a/gramps/gen/filters/rules/family/_isbookmarked.py b/gramps/gen/filters/rules/family/_isbookmarked.py index d3cfdf40c26..e617d7e54fe 100644 --- a/gramps/gen/filters/rules/family/_isbookmarked.py +++ b/gramps/gen/filters/rules/family/_isbookmarked.py @@ -35,6 +35,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Family +from ....db import Database + + # ------------------------------------------------------------------------- # # IsBookmarked @@ -47,8 +57,8 @@ class IsBookmarked(Rule): category = _("General filters") description = _("Matches the families on the bookmark list") - def prepare(self, db, user): - self.bookmarks = db.get_family_bookmarks().get() + def prepare(self, db: Database, user): + self.selected_handles: Set[str] = set(list(db.get_family_bookmarks().get())) - def apply(self, db, family): - return family.get_handle() in self.bookmarks + def apply_to_one(self, db: Database, family: Family) -> bool: + return family.handle in self.selected_handles diff --git a/gramps/gen/filters/rules/family/_isdescendantof.py b/gramps/gen/filters/rules/family/_isdescendantof.py index 52b0bc48aca..f33916f7acd 100644 --- a/gramps/gen/filters/rules/family/_isdescendantof.py +++ b/gramps/gen/filters/rules/family/_isdescendantof.py @@ -31,6 +31,16 @@ from .. import Rule from ....const import GRAMPS_LOCALE as glocale +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Family +from ....db import Database + + _ = glocale.translation.gettext @@ -49,30 +59,30 @@ class IsDescendantOf(Rule): category = _("General filters") description = _("Matches descendant families of the specified family") - def prepare(self, db, user): - self.map = set() + def prepare(self, db: Database, user): + self.selected_handles: Set[str] = set() first = False if int(self.list[1]) else True root_family = db.get_family_from_gramps_id(self.list[0]) self.init_list(db, root_family, first) def reset(self): - self.map.clear() + self.selected_handles.clear() - def apply(self, db, family): - return family.handle in self.map + def apply_to_one(self, db: Database, family: Family) -> bool: + return family.handle in self.selected_handles - def init_list(self, db, family, first): + def init_list(self, db: Database, family: Family, first: bool) -> None: """ Initialise family handle list. """ if not family: return if not first: - self.map.add(family.handle) + self.selected_handles.add(family.handle) - for child_ref in family.get_child_ref_list(): + for child_ref in family.child_ref_list: child = db.get_person_from_handle(child_ref.ref) if child: - for family_handle in child.get_family_handle_list(): + for family_handle in child.family_list: child_family = db.get_family_from_handle(family_handle) - self.init_list(db, child_family, 0) + self.init_list(db, child_family, False) diff --git a/gramps/gen/filters/rules/family/_memberbase.py b/gramps/gen/filters/rules/family/_memberbase.py deleted file mode 100644 index a042484ca2f..00000000000 --- a/gramps/gen/filters/rules/family/_memberbase.py +++ /dev/null @@ -1,60 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2002-2006 Donald N. Allingham -# -# 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. -# - -""" -Set of wrappers for family filter rules based on personal rules. - -Any rule that matches family based on personal rule applied -to father, mother, or any child, just needs to do two things: -> Set the class attribute 'base_class' to the personal rule -> Set apply method to be an appropriate wrapper below -Example: -in the class body, outside any method: -> base_class = SearchName -> apply = child_base -""" - - -def father_base(self, db, family): - father_handle = family.get_father_handle() - if father_handle: - father = db.get_person_from_handle(father_handle) - if father: - return self.base_class.apply(self, db, father) - else: - return False - - -def mother_base(self, db, family): - mother_handle = family.get_mother_handle() - if mother_handle: - mother = db.get_person_from_handle(mother_handle) - if mother: - return self.base_class.apply(self, db, mother) - else: - return False - - -def child_base(self, db, family): - for child_ref in family.get_child_ref_list(): - child = db.get_person_from_handle(child_ref.ref) - if self.base_class.apply(self, db, child): - return True - return False diff --git a/gramps/gen/filters/rules/family/_motherhasidof.py b/gramps/gen/filters/rules/family/_motherhasidof.py index 0c8ea2f6ede..f3f9dcaadb9 100644 --- a/gramps/gen/filters/rules/family/_motherhasidof.py +++ b/gramps/gen/filters/rules/family/_motherhasidof.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from .. import RegExpIdBase -from ._memberbase import mother_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -48,5 +56,13 @@ class MotherHasIdOf(RegExpIdBase): name = _("Families having mother with Id containing ") description = _("Matches families whose mother has a specified " "Gramps ID") category = _("Mother filters") - base_class = RegExpIdBase - apply = mother_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + mother_handle = family.mother_handle + if mother_handle: + mother = db.get_person_from_handle(mother_handle) + if mother: + return super().apply_to_one(db, mother) + else: + return False + return False diff --git a/gramps/gen/filters/rules/family/_motherhasnameof.py b/gramps/gen/filters/rules/family/_motherhasnameof.py index 1872cd46c3a..bed5e5a9249 100644 --- a/gramps/gen/filters/rules/family/_motherhasnameof.py +++ b/gramps/gen/filters/rules/family/_motherhasnameof.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from ..person import HasNameOf -from ._memberbase import mother_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -47,5 +55,13 @@ class MotherHasNameOf(HasNameOf): name = _("Families with mother with the ") description = _("Matches families whose mother has a specified " "(partial) name") category = _("Mother filters") - base_class = HasNameOf - apply = mother_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + mother_handle = family.mother_handle + if mother_handle: + mother = db.get_person_from_handle(mother_handle) + if mother: + return super().apply_to_one(db, mother) + else: + return False + return False diff --git a/gramps/gen/filters/rules/family/_regexpchildname.py b/gramps/gen/filters/rules/family/_regexpchildname.py index f0fb746b1c8..7493cf7662e 100644 --- a/gramps/gen/filters/rules/family/_regexpchildname.py +++ b/gramps/gen/filters/rules/family/_regexpchildname.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from ..person import RegExpName -from ._memberbase import child_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -50,5 +58,10 @@ class RegExpChildName(RegExpName): "that matches a specified regular expression" ) category = _("Child filters") - base_class = RegExpName - apply = child_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + for child_ref in family.child_ref_list: + child = db.get_person_from_handle(child_ref.ref) + if super().apply_to_one(db, child): + return True + return False diff --git a/gramps/gen/filters/rules/family/_regexpfathername.py b/gramps/gen/filters/rules/family/_regexpfathername.py index 6fe0e9dddef..dcefc777635 100644 --- a/gramps/gen/filters/rules/family/_regexpfathername.py +++ b/gramps/gen/filters/rules/family/_regexpfathername.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from ..person import RegExpName -from ._memberbase import father_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -50,5 +58,13 @@ class RegExpFatherName(RegExpName): "matching a specified regular expression" ) category = _("Father filters") - base_class = RegExpName - apply = father_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + father_handle = family.father_handle + if father_handle: + father = db.get_person_from_handle(father_handle) + if father: + return super().apply_to_one(db, father) + else: + return False + return False diff --git a/gramps/gen/filters/rules/family/_regexpmothername.py b/gramps/gen/filters/rules/family/_regexpmothername.py index 24378f10757..d1be98afd71 100644 --- a/gramps/gen/filters/rules/family/_regexpmothername.py +++ b/gramps/gen/filters/rules/family/_regexpmothername.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from ..person import RegExpName -from ._memberbase import mother_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -50,5 +58,13 @@ class RegExpMotherName(RegExpName): "matching a specified regular expression" ) category = _("Mother filters") - base_class = RegExpName - apply = mother_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + mother_handle = family.mother_handle + if mother_handle: + mother = db.get_person_from_handle(mother_handle) + if mother: + return super().apply_to_one(db, mother) + else: + return False + return False diff --git a/gramps/gen/filters/rules/family/_searchchildname.py b/gramps/gen/filters/rules/family/_searchchildname.py index 2450430a287..fe6336cad40 100644 --- a/gramps/gen/filters/rules/family/_searchchildname.py +++ b/gramps/gen/filters/rules/family/_searchchildname.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from ..person import SearchName -from ._memberbase import child_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -49,5 +57,10 @@ class SearchChildName(SearchName): "Matches families where any child has a specified " "(partial) name" ) category = _("Child filters") - base_class = SearchName - apply = child_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + for child_ref in family.child_ref_list: + child = db.get_person_from_handle(child_ref.ref) + if super().apply_to_one(db, child): + return True + return False diff --git a/gramps/gen/filters/rules/family/_searchfathername.py b/gramps/gen/filters/rules/family/_searchfathername.py index 431717d8cbe..f1ed44dce9d 100644 --- a/gramps/gen/filters/rules/family/_searchfathername.py +++ b/gramps/gen/filters/rules/family/_searchfathername.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from ..person import SearchName -from ._memberbase import father_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -47,5 +55,13 @@ class SearchFatherName(SearchName): name = _("Families with father matching the ") description = _("Matches families whose father has a specified " "(partial) name") category = _("Father filters") - base_class = SearchName - apply = father_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + father_handle = family.father_handle + if father_handle: + father = db.get_person_from_handle(father_handle) + if father: + return super().apply_to_one(db, father) + else: + return False + return False diff --git a/gramps/gen/filters/rules/family/_searchmothername.py b/gramps/gen/filters/rules/family/_searchmothername.py index 8bbca710488..cc2f7d7aacf 100644 --- a/gramps/gen/filters/rules/family/_searchmothername.py +++ b/gramps/gen/filters/rules/family/_searchmothername.py @@ -33,7 +33,15 @@ # # ------------------------------------------------------------------------- from ..person import SearchName -from ._memberbase import mother_base + + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Family +from ....db import Database # ------------------------------------------------------------------------- @@ -47,5 +55,13 @@ class SearchMotherName(SearchName): name = _("Families with mother matching the ") description = _("Matches families whose mother has a specified " "(partial) name") category = _("Mother filters") - base_class = SearchName - apply = mother_base + + def apply_to_one(self, db: Database, family: Family) -> bool: # type: ignore[override] + mother_handle = family.mother_handle + if mother_handle: + mother = db.get_person_from_handle(mother_handle) + if mother: + return super().apply_to_one(db, mother) + else: + return False + return False diff --git a/gramps/gen/filters/rules/media/_hasmedia.py b/gramps/gen/filters/rules/media/_hasmedia.py index 49ffc7f3975..ff69326c7b7 100644 --- a/gramps/gen/filters/rules/media/_hasmedia.py +++ b/gramps/gen/filters/rules/media/_hasmedia.py @@ -36,6 +36,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Media +from ....db import Database + + # ------------------------------------------------------------------------- # # HasMedia @@ -55,7 +64,7 @@ class HasMedia(Rule): category = _("General filters") allow_regex = True - def prepare(self, db, user): + def prepare(self, db: Database, user): self.date = None try: if self.list[3]: @@ -63,18 +72,18 @@ def prepare(self, db, user): except: pass - def apply(self, db, obj): - if not self.match_substring(0, obj.get_description()): + def apply_to_one(self, db: Database, obj: Media) -> bool: + if not self.match_substring(0, obj.desc): return False - if not self.match_substring(1, obj.get_mime_type()): + if not self.match_substring(1, obj.mime): return False - if not self.match_substring(2, obj.get_path()): + if not self.match_substring(2, obj.path): return False if self.date: - if not obj.get_date_object().match(self.date): + if not obj.date.match(self.date): return False return True diff --git a/gramps/gen/filters/rules/note/_hasnote.py b/gramps/gen/filters/rules/note/_hasnote.py index d9ed302d0a7..ad915c924f6 100644 --- a/gramps/gen/filters/rules/note/_hasnote.py +++ b/gramps/gen/filters/rules/note/_hasnote.py @@ -34,6 +34,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Note +from ....db import Database + + # ------------------------------------------------------------------------- # # HasNote @@ -57,7 +66,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.note_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -65,11 +74,11 @@ def prepare(self, db, user): self.note_type = NoteType() self.note_type.set_from_xml_str(self.list[1]) - def apply(self, _db, obj): + def apply_to_one(self, _db: Database, obj: Note) -> bool: """ Apply the rule. Return True on a match. """ - if not self.match_substring(0, obj.get()): + if not self.match_substring(0, str(obj.text)): return False if self.note_type: diff --git a/gramps/gen/filters/rules/note/_hastype.py b/gramps/gen/filters/rules/note/_hastype.py index d81f522b8d0..355717debf9 100644 --- a/gramps/gen/filters/rules/note/_hastype.py +++ b/gramps/gen/filters/rules/note/_hastype.py @@ -34,6 +34,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Note +from ....db import Database + + # ------------------------------------------------------------------------- # # HasType @@ -53,7 +62,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.note_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -61,10 +70,10 @@ def prepare(self, db, user): self.note_type = NoteType() self.note_type.set_from_xml_str(self.list[0]) - def apply(self, _db, obj): + def apply_to_one(self, _db: Database, obj: Note) -> bool: """ Apply the rule. Return True on a match. """ if self.note_type: - return obj.get_type() == self.note_type + return obj.type == self.note_type return False diff --git a/gramps/gen/filters/rules/note/_matchesregexpof.py b/gramps/gen/filters/rules/note/_matchesregexpof.py index ad8c5ada03f..e603e16117d 100644 --- a/gramps/gen/filters/rules/note/_matchesregexpof.py +++ b/gramps/gen/filters/rules/note/_matchesregexpof.py @@ -37,6 +37,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Note +from ....db import Database + + # ------------------------------------------------------------------------- # Notes that contain a substring or match a regular expression # ------------------------------------------------------------------------- @@ -49,8 +58,6 @@ class MatchesRegexpOf(Rule): category = _("General filters") allow_regex = True - def apply(self, db, note): + def apply_to_one(self, db: Database, note: Note) -> bool: """Apply the filter""" - if self.match_substring(0, note.get()): - return True - return False + return self.match_substring(0, str(note.text)) diff --git a/gramps/gen/filters/rules/note/_matchessubstringof.py b/gramps/gen/filters/rules/note/_matchessubstringof.py index 96cafb9fb0f..4aea59c921f 100644 --- a/gramps/gen/filters/rules/note/_matchessubstringof.py +++ b/gramps/gen/filters/rules/note/_matchessubstringof.py @@ -36,6 +36,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Note +from ....db import Database + + # ------------------------------------------------------------------------- # "Events having notes that contain a substring" # ------------------------------------------------------------------------- @@ -47,9 +56,9 @@ class MatchesSubstringOf(Rule): description = _("Matches notes that contain text " "which matches a substring") category = _("General filters") - def apply(self, db, note): + def apply_to_one(self, db: Database, note: Note) -> bool: """Apply the filter""" - text = note.get() + text = str(note.text) if text.upper().find(self.list[0].upper()) != -1: return True return False diff --git a/gramps/gen/filters/rules/person/_deeprelationshippathbetween.py b/gramps/gen/filters/rules/person/_deeprelationshippathbetween.py index 83dbca64b3e..ac89c6e8d96 100644 --- a/gramps/gen/filters/rules/person/_deeprelationshippathbetween.py +++ b/gramps/gen/filters/rules/person/_deeprelationshippathbetween.py @@ -36,6 +36,16 @@ from . import MatchesFilter from ....const import GRAMPS_LOCALE as glocale +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Union, List, Set, Dict +from ....lib import Person +from ....db import Database +from ....types import FamilyHandle, PersonHandle + _ = glocale.translation.gettext # ------------------------------------------------------------------------- # @@ -44,39 +54,45 @@ # ------------------------------------------------------------------------- -def get_family_handle_people(db, exclude_handle, family_handle): - people = set() +def get_family_handle_people( + db: Database, exclude_handle: str, family_handle: FamilyHandle +): + people: Set[str] = set() family = db.get_family_from_handle(family_handle) - def possibly_add_handle(h): + def possibly_add_handle(h: str): if h is not None and h != exclude_handle: people.add(h) - possibly_add_handle(family.get_father_handle()) - possibly_add_handle(family.get_mother_handle()) + possibly_add_handle(family.father_handle) + possibly_add_handle(family.mother_handle) - for child_ref in family.get_child_ref_list(): + for child_ref in family.child_ref_list: if child_ref: - possibly_add_handle(child_ref.get_reference_handle()) + possibly_add_handle(child_ref.ref) return people -def get_person_family_people(db, person, person_handle): - people = set() +def get_person_family_people( + db: Database, person: Person, person_handle: PersonHandle +) -> Set[str]: + people: Set[str] = set() - def add_family_handle_list(fam_list): + def add_family_handle_list(fam_list: List[FamilyHandle]): for family_handle in fam_list: people.update(get_family_handle_people(db, person_handle, family_handle)) - add_family_handle_list(person.get_family_handle_list()) - add_family_handle_list(person.get_parent_family_handle_list()) + add_family_handle_list(person.family_list) + add_family_handle_list(person.parent_family_list) return people -def find_deep_relations(db, user, person, target_people): +def find_deep_relations( + db: Database, user, person: Person, target_people: List[str] +) -> Set[str]: """This explores all possible paths between a person and one or more targets. The algorithm processes paths in a breadth first wave, one remove at a time. The first path that reaches a target causes the target @@ -85,12 +101,12 @@ def find_deep_relations(db, user, person, target_people): The function stores to do data and intermediate results in an ordered dict, rather than using a recursive algorithm because some trees have been found that exceed the standard python recursive depth.""" - return_paths = set() # all people in paths between targets and person + return_paths: Set[str] = set() # all people in paths between targets and person if person is None: return return_paths todo = deque([person.handle]) # list of work to do, handles, add to right, # pop from left - done = {} # The key records handles already examined, + done: Dict[str, Union[str, None]] = {} # The key records handles already examined, # the value is a handle of the previous person in the path, or None at # head of path. This forms a linked list of handles along the path. done[person.handle] = None @@ -142,7 +158,7 @@ class DeepRelationshipPathBetween(Rule): " the shortest path." ) - def prepare(self, db, user): + def prepare(self, db: Database, user): root_person_id = self.list[0] root_person = db.get_person_from_gramps_id(root_person_id) @@ -157,10 +173,9 @@ def prepare(self, db, user): db.get_number_of_people(), ) target_people = [] - for handle in db.iter_person_handles(): - person = db.get_person_from_handle(handle) - if self.filt.apply(db, person): - target_people.append(handle) + for person in db.iter_people(): + if self.filt.apply_to_one(db, person): + target_people.append(person.handle) if user: user.step_progress() if user: @@ -170,13 +185,15 @@ def prepare(self, db, user): _("Evaluating people"), db.get_number_of_people(), ) - self.__matches = find_deep_relations(db, user, root_person, target_people) + self.selected_handles: Set[str] = find_deep_relations( + db, user, root_person, target_people + ) if user: user.end_progress() def reset(self): self.filt.requestreset() - self.__matches = set() + self.selected_handles.clear() - def apply(self, db, person): - return person.get_handle() in self.__matches + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles diff --git a/gramps/gen/filters/rules/person/_disconnected.py b/gramps/gen/filters/rules/person/_disconnected.py index 66b60c28763..451c15f6a51 100644 --- a/gramps/gen/filters/rules/person/_disconnected.py +++ b/gramps/gen/filters/rules/person/_disconnected.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # Disconnected @@ -50,7 +59,5 @@ class Disconnected(Rule): "to any other person in the database" ) - def apply(self, db, person): - return not ( - person.get_parent_family_handle_list() or person.get_family_handle_list() - ) + def apply_to_one(self, db: Database, person: Person) -> bool: + return not (person.parent_family_list or person.family_list) diff --git a/gramps/gen/filters/rules/person/_everyone.py b/gramps/gen/filters/rules/person/_everyone.py index 29832d2d676..ea035a5ce44 100644 --- a/gramps/gen/filters/rules/person/_everyone.py +++ b/gramps/gen/filters/rules/person/_everyone.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # Everyone @@ -50,5 +59,5 @@ class Everyone(Rule): def is_empty(self): return True - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: return True diff --git a/gramps/gen/filters/rules/person/_familywithincompleteevent.py b/gramps/gen/filters/rules/person/_familywithincompleteevent.py index c5ff2bdb3f8..c91ede2fccd 100644 --- a/gramps/gen/filters/rules/person/_familywithincompleteevent.py +++ b/gramps/gen/filters/rules/person/_familywithincompleteevent.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # "Families with incomplete events" # ------------------------------------------------------------------------- @@ -47,15 +56,15 @@ class FamilyWithIncompleteEvent(Rule): ) category = _("Event filters") - def apply(self, db, person): - for family_handle in person.get_family_handle_list(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for family_handle in person.family_list: family = db.get_family_from_handle(family_handle) if family: - for event_ref in family.get_event_ref_list(): + for event_ref in family.event_ref_list: if event_ref: event = db.get_event_from_handle(event_ref.ref) - if not event.get_place_handle(): + if not event.place: return True - if not event.get_date_object(): + if not event.date: return True return False diff --git a/gramps/gen/filters/rules/person/_hasaddress.py b/gramps/gen/filters/rules/person/_hasaddress.py index 6c8b891aec8..fd69d849c7d 100644 --- a/gramps/gen/filters/rules/person/_hasaddress.py +++ b/gramps/gen/filters/rules/person/_hasaddress.py @@ -39,6 +39,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasAddress @@ -52,7 +61,7 @@ class HasAddress(Rule): description = _("Matches people with a certain number of personal addresses") category = _("General filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): # things we want to do just once, not for every handle if self.list[1] == "less than": self.count_type = 0 @@ -63,8 +72,8 @@ def prepare(self, db, user): self.userSelectedCount = int(self.list[0]) - def apply(self, db, person): - count = len(person.get_address_list()) + def apply_to_one(self, db: Database, person: Person) -> bool: + count = len(person.address_list) if self.count_type == 0: # "less than" return count < self.userSelectedCount elif self.count_type == 2: # "greater than" diff --git a/gramps/gen/filters/rules/person/_hasaddresstext.py b/gramps/gen/filters/rules/person/_hasaddresstext.py index a42b973634e..08d04d23b6d 100644 --- a/gramps/gen/filters/rules/person/_hasaddresstext.py +++ b/gramps/gen/filters/rules/person/_hasaddresstext.py @@ -32,6 +32,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasAddressText @@ -48,8 +57,8 @@ class HasAddressText(Rule): category = _("General filters") allow_regex = True - def apply(self, db, person): - for address in person.get_address_list(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for address in person.address_list: for string in address.get_text_data_list(): if self.match_substring(0, string): return True diff --git a/gramps/gen/filters/rules/person/_hasalternatename.py b/gramps/gen/filters/rules/person/_hasalternatename.py index 673111879e6..56814304776 100644 --- a/gramps/gen/filters/rules/person/_hasalternatename.py +++ b/gramps/gen/filters/rules/person/_hasalternatename.py @@ -36,6 +36,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasAlternateName @@ -48,8 +57,5 @@ class HasAlternateName(Rule): description = _("Matches people with an alternate name") category = _("General filters") - def apply(self, db, person): - if person.get_alternate_names(): - return True - else: - return False + def apply_to_one(self, db: Database, person: Person) -> bool: + return True if person.alternate_names else False diff --git a/gramps/gen/filters/rules/person/_hasassociation.py b/gramps/gen/filters/rules/person/_hasassociation.py index e1d95f8c310..dfb1972499a 100644 --- a/gramps/gen/filters/rules/person/_hasassociation.py +++ b/gramps/gen/filters/rules/person/_hasassociation.py @@ -39,6 +39,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasAssociation @@ -52,7 +61,7 @@ class HasAssociation(Rule): description = _("Matches people with a certain number of associations") category = _("General filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): # things we want to do just once, not for every handle if self.list[1] == "less than": self.count_type = 0 @@ -63,8 +72,8 @@ def prepare(self, db, user): self.selected_count = int(self.list[0]) - def apply(self, db, person): - count = len(person.get_person_ref_list()) + def apply_to_one(self, db: Database, person: Person) -> bool: + count = len(person.person_ref_list) if self.count_type == 0: # "less than" return count < self.selected_count elif self.count_type == 2: # "greater than" diff --git a/gramps/gen/filters/rules/person/_hasbirth.py b/gramps/gen/filters/rules/person/_hasbirth.py index 511a61fa829..bbf2ffd138d 100644 --- a/gramps/gen/filters/rules/person/_hasbirth.py +++ b/gramps/gen/filters/rules/person/_hasbirth.py @@ -39,6 +39,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasBirth @@ -59,26 +68,26 @@ def prepare(self, db, user): else: self.date = None - def apply(self, db, person): - for event_ref in person.get_event_ref_list(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for event_ref in person.event_ref_list: if not event_ref: continue - elif event_ref.role != EventRoleType.PRIMARY: + elif event_ref.role.value != EventRoleType.PRIMARY: # Only match primaries, no witnesses continue event = db.get_event_from_handle(event_ref.ref) - if event.get_type() != EventType.BIRTH: + if event.type != EventType.BIRTH: # No match: wrong type continue - if not self.match_substring(2, event.get_description()): + if not self.match_substring(2, event.description): # No match: wrong description continue if self.date: - if not event.get_date_object().match(self.date): + if not event.date.match(self.date): # No match: wrong date continue if self.list[1]: - place_id = event.get_place_handle() + place_id = event.place if place_id: place = db.get_place_from_handle(place_id) place_title = place_displayer.display(db, place) diff --git a/gramps/gen/filters/rules/person/_hascommonancestorwith.py b/gramps/gen/filters/rules/person/_hascommonancestorwith.py index cba92f5df10..33512003717 100644 --- a/gramps/gen/filters/rules/person/_hascommonancestorwith.py +++ b/gramps/gen/filters/rules/person/_hascommonancestorwith.py @@ -36,6 +36,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Dict, Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasCommonAncestorWith @@ -51,13 +61,13 @@ class HasCommonAncestorWith(Rule): "Matches people that have a common ancestor " "with a specified person" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db # For each(!) person we keep track of who their ancestors # are, in a set(). So we only have to compute a person's # ancestor list once. # Start with filling the cache for root person (gramps_id in self.list[0]) - self.ancestor_cache = {} + self.ancestor_cache: Dict[str, Set[str]] = {} root_person = db.get_person_from_gramps_id(self.list[0]) if root_person: self.add_ancs(db, root_person) @@ -65,7 +75,7 @@ def prepare(self, db, user): else: self.with_people = [] - def add_ancs(self, db, person): + def add_ancs(self, db: Database, person: Person): if person and person.handle not in self.ancestor_cache: self.ancestor_cache[person.handle] = set() # We are going to compare ancestors of one person with that of @@ -76,11 +86,11 @@ def add_ancs(self, db, person): else: return - for fam_handle in person.get_parent_family_handle_list(): + for fam_handle in person.parent_family_list: parentless_fam = True fam = db.get_family_from_handle(fam_handle) if fam: - for par_handle in (fam.get_father_handle(), fam.get_mother_handle()): + for par_handle in (fam.father_handle, fam.mother_handle): if par_handle: parentless_fam = False par = db.get_person_from_handle(par_handle) @@ -96,17 +106,21 @@ def add_ancs(self, db, person): def reset(self): self.ancestor_cache = {} - def has_common_ancestor(self, other): + def has_common_ancestor(self, other: Person): for handle in self.with_people: - if (handle in self.ancestor_cache and self.ancestor_cache[handle]) & ( + left_and = ( + handle in self.ancestor_cache and self.ancestor_cache[handle] + ) # type: ignore + right_and = ( other and other.handle in self.ancestor_cache and self.ancestor_cache[other.handle] - ): + ) # type: ignore + if left_and.intersection(right_and): # type: ignore return True return False - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: if person and person.handle not in self.ancestor_cache: self.add_ancs(db, person) diff --git a/gramps/gen/filters/rules/person/_hascommonancestorwithfiltermatch.py b/gramps/gen/filters/rules/person/_hascommonancestorwithfiltermatch.py index 78183cfa992..dccba95fa0a 100644 --- a/gramps/gen/filters/rules/person/_hascommonancestorwithfiltermatch.py +++ b/gramps/gen/filters/rules/person/_hascommonancestorwithfiltermatch.py @@ -73,15 +73,14 @@ def prepare(self, db, user): _("Retrieving all sub-filter matches"), db.get_number_of_people(), ) - for handle in db.iter_person_handles(): - person = db.get_person_from_handle(handle) + for person in db.iter_people(): if user: user.step_progress() - if person and self.filt.apply(db, person): + if person and self.filt.apply_to_one(db, person): # store all people in the filter so as to compare later self.with_people.append(person.handle) # fill list of ancestor of person if not present yet - if handle not in self.ancestor_cache: + if person.handle not in self.ancestor_cache: self.add_ancs(db, person) if user: user.end_progress() diff --git a/gramps/gen/filters/rules/person/_hasdeath.py b/gramps/gen/filters/rules/person/_hasdeath.py index d22ba288812..481cf13edfc 100644 --- a/gramps/gen/filters/rules/person/_hasdeath.py +++ b/gramps/gen/filters/rules/person/_hasdeath.py @@ -39,6 +39,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasDeath @@ -53,32 +62,32 @@ class HasDeath(Rule): category = _("Event filters") allow_regex = True - def prepare(self, db, user): + def prepare(self, db: Database, user): if self.list[0]: self.date = parser.parse(self.list[0]) else: self.date = None - def apply(self, db, person): - for event_ref in person.get_event_ref_list(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for event_ref in person.event_ref_list: if not event_ref: continue - elif event_ref.role != EventRoleType.PRIMARY: + elif event_ref.role.value != EventRoleType.PRIMARY: # Only match primaries, no witnesses continue event = db.get_event_from_handle(event_ref.ref) - if event.get_type() != EventType.DEATH: + if event.type != EventType.DEATH: # No match: wrong type continue - if not self.match_substring(2, event.get_description()): + if not self.match_substring(2, event.description): # No match: wrong description continue if self.date: - if not event.get_date_object().match(self.date): + if not event.date.match(self.date): # No match: wrong date continue if self.list[1]: - place_id = event.get_place_handle() + place_id = event.place if place_id: place = db.get_place_from_handle(place_id) place_title = place_displayer.display(db, place) diff --git a/gramps/gen/filters/rules/person/_hasevent.py b/gramps/gen/filters/rules/person/_hasevent.py index c44bf4d6a0a..2e7ee0e4bfd 100644 --- a/gramps/gen/filters/rules/person/_hasevent.py +++ b/gramps/gen/filters/rules/person/_hasevent.py @@ -34,6 +34,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasEvent @@ -55,15 +64,15 @@ class HasEvent(HasEventBase): name = _("People with the personal ") description = _("Matches people with a personal event of a particular value") - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: # type: ignore[override] """ Apply the rule. Return True if a match. """ - for event_ref in person.get_event_ref_list(): - if int(self.list[5]) and event_ref.role != EventRoleType.PRIMARY: + for event_ref in person.event_ref_list: + if int(self.list[5]) and event_ref.role.value != EventRoleType.PRIMARY: # Only match primaries, no witnesses continue event = db.get_event_from_handle(event_ref.ref) - if HasEventBase.apply(self, db, event): + if HasEventBase.apply_to_one(self, db, event): return True return False diff --git a/gramps/gen/filters/rules/person/_hasfamilyattribute.py b/gramps/gen/filters/rules/person/_hasfamilyattribute.py index e8707b392d7..46b9eff4845 100644 --- a/gramps/gen/filters/rules/person/_hasfamilyattribute.py +++ b/gramps/gen/filters/rules/person/_hasfamilyattribute.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasFamilyAttribute @@ -49,17 +58,17 @@ class HasFamilyAttribute(Rule): category = _("General filters") allow_regex = True - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: if not self.list[0]: return False - for f_id in person.get_family_handle_list(): + for f_id in person.family_list: f = db.get_family_from_handle(f_id) if not f: continue - for attr in f.get_attribute_list(): + for attr in f.attribute_list: if attr: - name_match = self.list[0] == attr.get_type() - value_match = self.match_substring(1, attr.get_value()) + name_match = self.list[0] == attr.type + value_match = self.match_substring(1, attr.value) if name_match and value_match: return True return False diff --git a/gramps/gen/filters/rules/person/_hasfamilyevent.py b/gramps/gen/filters/rules/person/_hasfamilyevent.py index 6b07bc9ad2d..991c9934398 100644 --- a/gramps/gen/filters/rules/person/_hasfamilyevent.py +++ b/gramps/gen/filters/rules/person/_hasfamilyevent.py @@ -36,6 +36,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasFamilyEvent @@ -57,7 +66,7 @@ def __init__(self, arg, use_regex=False, use_case=False): self.date = None self.event_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): if self.list[0]: self.event_type = EventType() self.event_type.set_from_xml_str(self.list[0]) @@ -67,22 +76,22 @@ def prepare(self, db, user): except: pass - def apply(self, db, person): - for handle in person.get_family_handle_list(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for handle in person.family_list: family = db.get_family_from_handle(handle) - for event_ref in family.get_event_ref_list(): + for event_ref in family.event_ref_list: event = db.get_event_from_handle(event_ref.ref) val = 1 if self.event_type and event.type != self.event_type: val = 0 if self.list[3]: - if not self.match_substring(3, event.get_description()): + if not self.match_substring(3, event.description): val = 0 if self.date: - if not event.get_date_object().match(self.date): + if not event.date.match(self.date): val = 0 if self.list[2]: - place_id = event.get_place_handle() + place_id = event.place if place_id: place = db.get_place_from_handle(place_id) place_title = place_displayer.display(db, place) diff --git a/gramps/gen/filters/rules/person/_hasnameof.py b/gramps/gen/filters/rules/person/_hasnameof.py index 2ef2c10dc40..310e9158047 100644 --- a/gramps/gen/filters/rules/person/_hasnameof.py +++ b/gramps/gen/filters/rules/person/_hasnameof.py @@ -37,6 +37,15 @@ from ....lib.nameorigintype import NameOriginType +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasNameOf @@ -63,43 +72,41 @@ class HasNameOf(Rule): category = _("General filters") allow_regex = True - def apply(self, db, person): - for name in [person.get_primary_name()] + person.get_alternate_names(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for name in [person.primary_name] + person.alternate_names: if self.match_name(name): return True return False def match_name(self, name): - if self.list[0] and not self.match_substring(0, name.get_first_name()): + if self.list[0] and not self.match_substring(0, name.first_name): return False elif self.list[1] and not self.match_substring(1, name.get_surname()): return False - elif self.list[2] and not self.match_substring(2, name.get_title()): + elif self.list[2] and not self.match_substring(2, name.title): return False - elif self.list[3] and not self.match_substring(3, name.get_suffix()): + elif self.list[3] and not self.match_substring(3, name.suffix): return False - elif self.list[4] and not self.match_substring(4, name.get_call_name()): + elif self.list[4] and not self.match_substring(4, name.call): return False - elif self.list[5] and not self.match_substring(5, name.get_nick_name()): + elif self.list[5] and not self.match_substring(5, name.nick): return False - elif self.list[10] and not self.match_substring( - 10, name.get_family_nick_name() - ): + elif self.list[10] and not self.match_substring(10, name.famnick): return False else: - for surn in name.get_surname_list(): + for surn in name.surname_list: if self.match_surname(surn): return True return False def match_surname(self, surn): - if self.list[6] and not self.match_substring(6, surn.get_prefix()): + if self.list[6] and not self.match_substring(6, surn.prefix): return False - if self.list[7] and not self.match_substring(7, surn.get_surname()): + if self.list[7] and not self.match_substring(7, surn.surname): return False - if self.list[8] and not self.match_substring(8, surn.get_connector()): + if self.list[8] and not self.match_substring(8, surn.connector): return False - if surn.get_origintype().value == NameOriginType.PATRONYMIC: - if self.list[9] and not self.match_substring(9, surn.get_surname()): + if int(surn.origintype) == NameOriginType.PATRONYMIC: + if self.list[9] and not self.match_substring(9, surn.surname): return False return True diff --git a/gramps/gen/filters/rules/person/_hasnameorigintype.py b/gramps/gen/filters/rules/person/_hasnameorigintype.py index 9e999495c94..69a02f0791c 100644 --- a/gramps/gen/filters/rules/person/_hasnameorigintype.py +++ b/gramps/gen/filters/rules/person/_hasnameorigintype.py @@ -34,6 +34,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasNameOriginType @@ -53,7 +62,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.name_origin_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -61,13 +70,13 @@ def prepare(self, db, user): self.name_origin_type = NameOriginType() self.name_origin_type.set_from_xml_str(self.list[0]) - def apply(self, _db, obj): + def apply_to_one(self, _db: Database, obj: Person) -> bool: """ Apply the rule. Return True on a match. """ if self.name_origin_type: - for name in [obj.get_primary_name()] + obj.get_alternate_names(): - for surname in name.get_surname_list(): - if surname.get_origintype() == self.name_origin_type: + for name in [obj.primary_name] + obj.alternate_names: + for surname in name.surname_list: + if surname.origintype == self.name_origin_type: return True return False diff --git a/gramps/gen/filters/rules/person/_hasnametype.py b/gramps/gen/filters/rules/person/_hasnametype.py index 9e8fa7c0115..dc78c9d8498 100644 --- a/gramps/gen/filters/rules/person/_hasnametype.py +++ b/gramps/gen/filters/rules/person/_hasnametype.py @@ -34,6 +34,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasNameType @@ -53,7 +62,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.name_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -61,12 +70,12 @@ def prepare(self, db, user): self.name_type = NameType() self.name_type.set_from_xml_str(self.list[0]) - def apply(self, _db, obj): + def apply_to_one(self, _db: Database, obj: Person) -> bool: """ Apply the rule. Return True on a match. """ if self.name_type: - for name in [obj.get_primary_name()] + obj.get_alternate_names(): - if name.get_type() == self.name_type: + for name in [obj.primary_name] + obj.alternate_names: + if name.type == self.name_type: return True return False diff --git a/gramps/gen/filters/rules/person/_hasnickname.py b/gramps/gen/filters/rules/person/_hasnickname.py index 48f7da04057..a61713bb1dc 100644 --- a/gramps/gen/filters/rules/person/_hasnickname.py +++ b/gramps/gen/filters/rules/person/_hasnickname.py @@ -36,6 +36,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasNickname @@ -48,7 +57,7 @@ class HasNickname(Rule): description = _("Matches people with a nickname") category = _("General filters") - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: if person.get_nick_name(): return True return False diff --git a/gramps/gen/filters/rules/person/_hasothergender.py b/gramps/gen/filters/rules/person/_hasothergender.py index 1d8533f03be..d602787537c 100644 --- a/gramps/gen/filters/rules/person/_hasothergender.py +++ b/gramps/gen/filters/rules/person/_hasothergender.py @@ -36,6 +36,14 @@ from ....lib.person import Person +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....db import Database + + # ------------------------------------------------------------------------- # # HasOtherGender @@ -48,5 +56,5 @@ class HasOtherGender(Rule): category = _("General filters") description = _("Matches all people with other gender") - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: return person.gender == Person.OTHER diff --git a/gramps/gen/filters/rules/person/_hasrelationship.py b/gramps/gen/filters/rules/person/_hasrelationship.py index 7223d991002..8b6a37e955f 100644 --- a/gramps/gen/filters/rules/person/_hasrelationship.py +++ b/gramps/gen/filters/rules/person/_hasrelationship.py @@ -34,6 +34,15 @@ _ = glocale.translation.gettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasRelationship @@ -57,7 +66,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.relationship_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -65,23 +74,20 @@ def prepare(self, db, user): self.relationship_type = FamilyRelType() self.relationship_type.set_from_xml_str(self.list[1]) - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: Person) -> bool: """ Apply the rule. Return True on a match. """ relationship_type = 0 total_children = 0 - number_relations = len(obj.get_family_handle_list()) + number_relations = len(obj.family_list) # count children and look for a relationship type match - for handle in obj.get_family_handle_list(): + for handle in obj.family_list: family = db.get_family_from_handle(handle) if family: - total_children += len(family.get_child_ref_list()) - if ( - self.relationship_type - and self.relationship_type == family.get_relationship() - ): + total_children += len(family.child_ref_list) + if self.relationship_type and (self.relationship_type == family.type): relationship_type = 1 # if number of relations specified diff --git a/gramps/gen/filters/rules/person/_hassoundexname.py b/gramps/gen/filters/rules/person/_hassoundexname.py index af3227acd6f..cc1f2734a7b 100644 --- a/gramps/gen/filters/rules/person/_hassoundexname.py +++ b/gramps/gen/filters/rules/person/_hassoundexname.py @@ -35,6 +35,15 @@ _ = glocale.translation.sgettext +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasSoundexName @@ -58,18 +67,18 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.soundex = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ if self.list[0]: self.soundex = soundex(self.list[0]) - def apply(self, _db, obj): + def apply_to_one(self, _db: Database, obj: Person) -> bool: """ Apply the rule. Return True on a match. """ - for name in [obj.get_primary_name()] + obj.get_alternate_names(): + for name in [obj.primary_name] + obj.alternate_names: if self._match_name(name): return True return False @@ -79,17 +88,17 @@ def _match_name(self, name): Match a name against the soundex. """ if self.soundex: - if soundex(str(name.get_first_name())) == self.soundex: + if soundex(str(name.first_name)) == self.soundex: return True if soundex(str(name.get_surname())) == self.soundex: return True - if soundex(str(name.get_call_name())) == self.soundex: + if soundex(str(name.call)) == self.soundex: return True - if soundex(str(name.get_nick_name())) == self.soundex: + if soundex(str(name.nick)) == self.soundex: return True - if soundex(str(name.get_family_nick_name())) == self.soundex: + if soundex(str(name.famnick)) == self.soundex: return True - for surname in name.get_surname_list(): + for surname in name.surname_list: if self._match_surname(surname): return True return False @@ -100,7 +109,7 @@ def _match_surname(self, surname): """ if soundex(str(surname.get_surname())) == self.soundex: return True - if surname.get_origintype().value == NameOriginType.PATRONYMIC: - if soundex(str(surname.get_surname())) == self.soundex: + if int(surname.origintype) == NameOriginType.PATRONYMIC: + if soundex(str(surname.surname)) == self.soundex: return True return False diff --git a/gramps/gen/filters/rules/person/_hastextmatchingsubstringof.py b/gramps/gen/filters/rules/person/_hastextmatchingsubstringof.py index 7aa4c2aaa78..a3379d630ee 100644 --- a/gramps/gen/filters/rules/person/_hastextmatchingsubstringof.py +++ b/gramps/gen/filters/rules/person/_hastextmatchingsubstringof.py @@ -40,6 +40,23 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database +from ....types import ( + PersonHandle, + EventHandle, + PlaceHandle, + MediaHandle, + FamilyHandle, +) + + # ------------------------------------------------------------------------- # "HasTextMatchingSubstringOf" # ------------------------------------------------------------------------- @@ -52,15 +69,15 @@ class HasTextMatchingSubstringOf(Rule): category = _("General filters") allow_regex = True - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.person_map = set() - self.event_map = set() - self.source_map = set() - self.repo_map = set() - self.family_map = set() - self.place_map = set() - self.media_map = set() + self.person_map: Set[str] = set() + self.event_map: Set[str] = set() + self.source_map: Set[str] = set() + self.repo_map: Set[str] = set() + self.family_map: Set[str] = set() + self.place_map: Set[str] = set() + self.media_map: Set[str] = set() try: if int(self.list[1]): self.case_sensitive = True @@ -80,35 +97,28 @@ def reset(self): self.place_map.clear() self.media_map.clear() - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: if person.handle in self.person_map: # Cached by matching Source? return True if self.match_object(person): # first match the person itself return True # Look for matching events - if any( - self.search_event(event_ref.ref) - for event_ref in person.get_event_ref_list() - ): + if any(self.search_event(event_ref.ref) for event_ref in person.event_ref_list): return True # Look for matching families if any( - self.search_family(family_handle) - for family_handle in person.get_family_handle_list() + self.search_family(family_handle) for family_handle in person.family_list ): return True # Look for matching media objects - if any( - self.search_media(media_ref.get_reference_handle()) - for media_ref in person.get_media_list() - ): + if any(self.search_media(media_ref.ref) for media_ref in person.media_list): return True return False - def search_family(self, family_handle): + def search_family(self, family_handle: FamilyHandle): if not family_handle: return False # search inside the family and cache the result to not search a family twice @@ -120,19 +130,18 @@ def search_family(self, family_handle): else: if any( self.search_event(event_ref.ref) - for event_ref in family.get_event_ref_list() + for event_ref in family.event_ref_list ): match = 1 if any( - self.search_media(media_ref.get_reference_handle()) - for media_ref in family.get_media_list() + self.search_media(media_ref.ref) for media_ref in family.media_list ): return True if match: self.family_map.add(family_handle) return family_handle in self.family_map - def search_event(self, event_handle): + def search_event(self, event_handle: EventHandle): if not event_handle: return False # search inside the event and cache the result (event sharing) @@ -142,19 +151,18 @@ def search_event(self, event_handle): if self.match_object(event): match = 1 elif event: - place_handle = event.get_place_handle() + place_handle = event.place if place_handle and self.search_place(place_handle): match = 1 if any( - self.search_media(media_ref.get_reference_handle()) - for media_ref in event.get_media_list() + self.search_media(media_ref.ref) for media_ref in event.media_list ): return True if match: self.event_map.add(event_handle) return event_handle in self.event_map - def search_place(self, place_handle): + def search_place(self, place_handle: PlaceHandle) -> bool: if not place_handle: return False # search inside the place and cache the result @@ -164,7 +172,7 @@ def search_place(self, place_handle): self.place_map.add(place_handle) return place_handle in self.place_map - def search_media(self, media_handle): + def search_media(self, media_handle: MediaHandle) -> bool: if not media_handle: return False # search inside the media object and cache the result @@ -191,10 +199,7 @@ def cache_sources(self): % (match, self.list[0], source.gramps_id) ) if not match: - if any( - reporef.get_reference_handle() in self.repo_map - for reporef in source.get_reporef_list() - ): + if any(reporef.ref in self.repo_map for reporef in source.reporef_list): match = True LOG.debug( "cache_sources repomatch %s string %s source %s" diff --git a/gramps/gen/filters/rules/person/_hasunknowngender.py b/gramps/gen/filters/rules/person/_hasunknowngender.py index 5c25eb8995e..fa7d9763bb9 100644 --- a/gramps/gen/filters/rules/person/_hasunknowngender.py +++ b/gramps/gen/filters/rules/person/_hasunknowngender.py @@ -36,6 +36,14 @@ from ....lib.person import Person +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....db import Database + + # ------------------------------------------------------------------------- # # HasUnknownGender @@ -48,5 +56,5 @@ class HasUnknownGender(Rule): category = _("General filters") description = _("Matches all people with unknown gender") - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: return person.gender == Person.UNKNOWN diff --git a/gramps/gen/filters/rules/person/_havealtfamilies.py b/gramps/gen/filters/rules/person/_havealtfamilies.py index 41c6b468273..e85edb7a01e 100644 --- a/gramps/gen/filters/rules/person/_havealtfamilies.py +++ b/gramps/gen/filters/rules/person/_havealtfamilies.py @@ -36,6 +36,15 @@ from ....lib.childreftype import ChildRefType +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # "People who were adopted" # ------------------------------------------------------------------------- @@ -46,18 +55,14 @@ class HaveAltFamilies(Rule): description = _("Matches people who were adopted") category = _("Family filters") - def apply(self, db, person): - for fhandle in person.get_parent_family_handle_list(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for fhandle in person.parent_family_list: family = db.get_family_from_handle(fhandle) if family: - ref = [ - ref - for ref in family.get_child_ref_list() - if ref.ref == person.handle - ] + ref = [ref for ref in family.child_ref_list if ref.ref == person.handle] if ( - ref[0].get_father_relation() == ChildRefType.ADOPTED - or ref[0].get_mother_relation() == ChildRefType.ADOPTED + ref[0].frel == ChildRefType.ADOPTED + or ref[0].mrel == ChildRefType.ADOPTED ): return True return False diff --git a/gramps/gen/filters/rules/person/_havechildren.py b/gramps/gen/filters/rules/person/_havechildren.py index 3753d7a8a3a..e68ac68bc9f 100644 --- a/gramps/gen/filters/rules/person/_havechildren.py +++ b/gramps/gen/filters/rules/person/_havechildren.py @@ -36,6 +36,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # "People with children" @@ -48,9 +57,9 @@ class HaveChildren(Rule): description = _("Matches people who have children") category = _("Family filters") - def apply(self, db, person): - for family_handle in person.get_family_handle_list(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for family_handle in person.family_list: family = db.get_family_from_handle(family_handle) - if family is not None and family.get_child_ref_list(): + if family is not None and family.child_ref_list: return True return False diff --git a/gramps/gen/filters/rules/person/_incompletenames.py b/gramps/gen/filters/rules/person/_incompletenames.py index 3e11c52b760..5399929bf1a 100644 --- a/gramps/gen/filters/rules/person/_incompletenames.py +++ b/gramps/gen/filters/rules/person/_incompletenames.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IncompleteNames @@ -47,13 +56,13 @@ class IncompleteNames(Rule): description = _("Matches people with firstname or lastname missing") category = _("General filters") - def apply(self, db, person): - for name in [person.get_primary_name()] + person.get_alternate_names(): - if name.get_first_name().strip() == "": + def apply_to_one(self, db: Database, person: Person) -> bool: + for name in [person.primary_name] + person.alternate_names: + if name.first_name.strip() == "": return True - if name.get_surname_list(): - for surn in name.get_surname_list(): - if surn.get_surname().strip() == "": + if name.surname_list: + for surn in name.surname_list: + if surn.surname.strip() == "": return True else: return True diff --git a/gramps/gen/filters/rules/person/_isancestorof.py b/gramps/gen/filters/rules/person/_isancestorof.py index ac2668b4a66..c4c4c4222e4 100644 --- a/gramps/gen/filters/rules/person/_isancestorof.py +++ b/gramps/gen/filters/rules/person/_isancestorof.py @@ -35,6 +35,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsAncestorOf @@ -48,14 +58,14 @@ class IsAncestorOf(Rule): category = _("Ancestral filters") description = _("Matches people that are ancestors of a specified person") - def prepare(self, db, user): + def prepare(self, db: Database, user): """Assume that if 'Inclusive' not defined, assume inclusive""" self.db = db - self.map = set() + self.selected_handles: Set[str] = set() try: - first = 0 if int(self.list[1]) else 1 + first = False if int(self.list[1]) else True except IndexError: - first = 1 + first = True try: root_person = db.get_person_from_gramps_id(self.list[0]) self.init_ancestor_list(db, root_person, first) @@ -63,26 +73,28 @@ def prepare(self, db, user): pass def reset(self): - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles - def init_ancestor_list(self, db, person, first): + def init_ancestor_list(self, db: Database, person: Person, first: bool) -> None: if not person: return - if person.handle in self.map: + if person.handle in self.selected_handles: return if not first: - self.map.add(person.handle) - fam_id = person.get_main_parents_family_handle() + self.selected_handles.add(person.handle) + fam_id = ( + person.parent_family_list[0] if len(person.parent_family_list) > 0 else None + ) if fam_id: fam = db.get_family_from_handle(fam_id) if fam: - f_id = fam.get_father_handle() - m_id = fam.get_mother_handle() + f_id = fam.father_handle + m_id = fam.mother_handle if f_id: - self.init_ancestor_list(db, db.get_person_from_handle(f_id), 0) + self.init_ancestor_list(db, db.get_person_from_handle(f_id), False) if m_id: - self.init_ancestor_list(db, db.get_person_from_handle(m_id), 0) + self.init_ancestor_list(db, db.get_person_from_handle(m_id), False) diff --git a/gramps/gen/filters/rules/person/_isancestoroffiltermatch.py b/gramps/gen/filters/rules/person/_isancestoroffiltermatch.py index ff7b6ea5bc2..3bc42d9403e 100644 --- a/gramps/gen/filters/rules/person/_isancestoroffiltermatch.py +++ b/gramps/gen/filters/rules/person/_isancestoroffiltermatch.py @@ -36,6 +36,16 @@ from ._matchesfilter import MatchesFilter +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsAncestorOfFilterMatch @@ -52,16 +62,16 @@ class IsAncestorOfFilterMatch(IsAncestorOf): "Matches people that are ancestors " "of anybody matched by a filter" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[str] = set() try: if int(self.list[1]): - first = 0 + first = False else: - first = 1 + first = True except IndexError: - first = 1 + first = True self.filt = MatchesFilter(self.list[0:1]) self.filt.requestprepare(db, user) @@ -74,14 +84,14 @@ def prepare(self, db, user): for person in db.iter_people(): if user: user.step_progress() - if self.filt.apply(db, person): + if self.filt.apply_to_one(db, person): self.init_ancestor_list(db, person, first) if user: user.end_progress() def reset(self): self.filt.requestreset() - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles diff --git a/gramps/gen/filters/rules/person/_isbookmarked.py b/gramps/gen/filters/rules/person/_isbookmarked.py index 715cefcca3f..50baf1bae45 100644 --- a/gramps/gen/filters/rules/person/_isbookmarked.py +++ b/gramps/gen/filters/rules/person/_isbookmarked.py @@ -36,6 +36,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsBookmarked @@ -48,8 +58,8 @@ class IsBookmarked(Rule): category = _("General filters") description = _("Matches the people on the bookmark list") - def prepare(self, db, user): - self.bookmarks = db.get_bookmarks().get() + def prepare(self, db: Database, user): + self.selected_handles: Set[str] = set(list(db.get_bookmarks().get())) - def apply(self, db, person): - return person.handle in self.bookmarks + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles diff --git a/gramps/gen/filters/rules/person/_ischildoffiltermatch.py b/gramps/gen/filters/rules/person/_ischildoffiltermatch.py index fa737f26522..55cb1ae9926 100644 --- a/gramps/gen/filters/rules/person/_ischildoffiltermatch.py +++ b/gramps/gen/filters/rules/person/_ischildoffiltermatch.py @@ -36,6 +36,16 @@ from ._matchesfilter import MatchesFilter +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsChildOfFilterMatch @@ -50,9 +60,9 @@ class IsChildOfFilterMatch(Rule): category = _("Family filters") description = _("Matches children of anybody matched by a filter") - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[str] = set() self.filt = MatchesFilter(self.list) self.filt.requestprepare(db, user) if user: @@ -64,22 +74,24 @@ def prepare(self, db, user): for person in db.iter_people(): if user: user.step_progress() - if self.filt.apply(db, person): + if self.filt.apply_to_one(db, person): self.init_list(person) if user: user.end_progress() def reset(self): self.filt.requestreset() - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles - def init_list(self, person): + def init_list(self, person: Person): if not person: return - for fam_id in person.get_family_handle_list(): + for fam_id in person.family_list: fam = self.db.get_family_from_handle(fam_id) if fam: - self.map.update(child_ref.ref for child_ref in fam.get_child_ref_list()) + self.selected_handles.update( + child_ref.ref for child_ref in fam.child_ref_list + ) diff --git a/gramps/gen/filters/rules/person/_isdefaultperson.py b/gramps/gen/filters/rules/person/_isdefaultperson.py index 2b58ed43d41..2fc409becd3 100644 --- a/gramps/gen/filters/rules/person/_isdefaultperson.py +++ b/gramps/gen/filters/rules/person/_isdefaultperson.py @@ -35,6 +35,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsDefaultPerson @@ -47,13 +57,11 @@ class IsDefaultPerson(Rule): category = _("General filters") description = _("Matches the Home Person") - def prepare(self, db, user): - p = db.get_default_person() + def prepare(self, db: Database, user): + self.selected_handles: Set[str] = set() + p: Person = db.get_default_person() if p: - self.def_handle = p.get_handle() - self.apply = self.apply_real - else: - self.apply = lambda db, p: False + self.selected_handles.add(p.handle) - def apply_real(self, db, person): - return person.handle == self.def_handle + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles diff --git a/gramps/gen/filters/rules/person/_isdescendantfamilyof.py b/gramps/gen/filters/rules/person/_isdescendantfamilyof.py index bc288b289e8..8c41486dff5 100644 --- a/gramps/gen/filters/rules/person/_isdescendantfamilyof.py +++ b/gramps/gen/filters/rules/person/_isdescendantfamilyof.py @@ -39,6 +39,17 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import List, Set +from ....lib import Person +from ....db import Database +from ....types import PersonHandle + + # ------------------------------------------------------------------------- # # IsDescendantFamilyOf @@ -56,9 +67,9 @@ class IsDescendantFamilyOf(Rule): "of a descendant of a specified person" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.matches = set() + self.selected_handles: Set[PersonHandle] = set() self.root_person = db.get_person_from_gramps_id(self.list[0]) self.add_matches(self.root_person) try: @@ -72,48 +83,48 @@ def prepare(self, db, user): self.exclude() def reset(self): - self.matches = set() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.matches + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles - def add_matches(self, person): + def add_matches(self, person: Person): if not person: return # Add self - expand = [person] + queue: List[Person] = [person] - while expand: - person = expand.pop(0) - if person is None or person.handle in self.matches: + while queue: + person = queue.pop(0) + if person is None or person.handle in self.selected_handles: # if we have been here before, skip continue - self.matches.add(person.handle) - for family_handle in person.get_family_handle_list(): + self.selected_handles.add(person.handle) + for family_handle in person.family_list: family = self.db.get_family_from_handle(family_handle) if family: # Add every child recursively - for child_ref in family.get_child_ref_list(): + for child_ref in family.child_ref_list: if child_ref: - expand.append(self.db.get_person_from_handle(child_ref.ref)) + queue.append(self.db.get_person_from_handle(child_ref.ref)) # Add spouse - if person.handle == family.get_father_handle(): - spouse_handle = family.get_mother_handle() + if person.handle == family.father_handle: + spouse_handle = family.mother_handle else: - spouse_handle = family.get_father_handle() - self.matches.add(spouse_handle) + spouse_handle = family.father_handle + self.selected_handles.add(spouse_handle) def exclude(self): # This removes root person and his/her spouses from the matches set if not self.root_person: return - self.matches.remove(self.root_person.handle) - for family_handle in self.root_person.get_family_handle_list(): + self.selected_handles.remove(self.root_person.handle) + for family_handle in self.root_person.family_list: family = self.db.get_family_from_handle(family_handle) if family: - if self.root_person.handle == family.get_father_handle(): - spouse_handle = family.get_mother_handle() + if self.root_person.handle == family.father_handle: + spouse_handle = family.mother_handle else: - spouse_handle = family.get_father_handle() - self.matches.remove(spouse_handle) + spouse_handle = family.father_handle + self.selected_handles.remove(spouse_handle) diff --git a/gramps/gen/filters/rules/person/_isdescendantfamilyoffiltermatch.py b/gramps/gen/filters/rules/person/_isdescendantfamilyoffiltermatch.py index 25634a1bc14..8b43af0ef36 100644 --- a/gramps/gen/filters/rules/person/_isdescendantfamilyoffiltermatch.py +++ b/gramps/gen/filters/rules/person/_isdescendantfamilyoffiltermatch.py @@ -23,6 +23,8 @@ # Standard Python modules # # ------------------------------------------------------------------------- +from typing import Set + from ....const import GRAMPS_LOCALE as glocale _ = glocale.translation.gettext @@ -34,6 +36,8 @@ # ------------------------------------------------------------------------- from ._isdescendantfamilyof import IsDescendantFamilyOf from ._matchesfilter import MatchesFilter +from ....types import PersonHandle +from ....db.generic import Database # ------------------------------------------------------------------------- @@ -53,9 +57,9 @@ class IsDescendantFamilyOfFilterMatch(IsDescendantFamilyOf): "of anybody matched by a filter" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.matches = set() + self.selected_handles: Set[PersonHandle] = set() self.matchfilt = MatchesFilter(self.list[0:1]) self.matchfilt.requestprepare(db, user) @@ -65,14 +69,14 @@ def prepare(self, db, user): _("Retrieving all sub-filter matches"), db.get_number_of_people(), ) - for person in db.iter_people(): + for handle, person in db._iter_raw_person_data(): if user: user.step_progress() - if self.matchfilt.apply(db, person): + if self.matchfilt.apply_to_one(db, person): self.add_matches(person) if user: user.end_progress() def reset(self): self.matchfilt.requestreset() - self.matches = set() + self.selected_handles.clear() diff --git a/gramps/gen/filters/rules/person/_isdescendantof.py b/gramps/gen/filters/rules/person/_isdescendantof.py index 52cea65c8f9..fb96ea08a0d 100644 --- a/gramps/gen/filters/rules/person/_isdescendantof.py +++ b/gramps/gen/filters/rules/person/_isdescendantof.py @@ -35,6 +35,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsDescendantOf @@ -49,9 +59,9 @@ class IsDescendantOf(Rule): category = _("Descendant filters") description = _("Matches all descendants for the specified person") - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[str] = set() try: first = False if int(self.list[1]) else True except IndexError: @@ -63,20 +73,20 @@ def prepare(self, db, user): pass def reset(self): - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles - def init_list(self, person, first): - if not person or person.handle in self.map: + def init_list(self, person: Person, first: bool): + if not person or person.handle in self.selected_handles: # if we have been here before, skip return if not first: - self.map.add(person.handle) + self.selected_handles.add(person.handle) - for fam_id in person.get_family_handle_list(): + for fam_id in person.family_list: fam = self.db.get_family_from_handle(fam_id) if fam: - for child_ref in fam.get_child_ref_list(): - self.init_list(self.db.get_person_from_handle(child_ref.ref), 0) + for child_ref in fam.child_ref_list: + self.init_list(self.db.get_person_from_handle(child_ref.ref), False) diff --git a/gramps/gen/filters/rules/person/_isdescendantoffiltermatch.py b/gramps/gen/filters/rules/person/_isdescendantoffiltermatch.py index dbb864b4b44..471e13a27a6 100644 --- a/gramps/gen/filters/rules/person/_isdescendantoffiltermatch.py +++ b/gramps/gen/filters/rules/person/_isdescendantoffiltermatch.py @@ -36,6 +36,16 @@ from ._matchesfilter import MatchesFilter +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsDescendantOfFilterMatch @@ -52,16 +62,16 @@ class IsDescendantOfFilterMatch(IsDescendantOf): "Matches people that are descendants " "of anybody matched by a filter" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[str] = set() try: if int(self.list[1]): - first = 0 + first = False else: - first = 1 + first = True except IndexError: - first = 1 + first = True self.filt = MatchesFilter(self.list[0:1]) self.filt.requestprepare(db, user) @@ -74,14 +84,14 @@ def prepare(self, db, user): for person in db.iter_people(): if user: user.step_progress() - if self.filt.apply(db, person): + if self.filt.apply_to_one(db, person): self.init_list(person, first) if user: user.end_progress() def reset(self): self.filt.requestreset() - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles diff --git a/gramps/gen/filters/rules/person/_isduplicatedancestorof.py b/gramps/gen/filters/rules/person/_isduplicatedancestorof.py index 9d31ca2e200..bb28c0806d6 100644 --- a/gramps/gen/filters/rules/person/_isduplicatedancestorof.py +++ b/gramps/gen/filters/rules/person/_isduplicatedancestorof.py @@ -36,6 +36,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsDuplicatedAncestorOf @@ -52,38 +62,40 @@ class IsDuplicatedAncestorOf(Rule): "Matches people that are ancestors twice or more " "of a specified person" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() - self.map2 = set() + self.cache: Set[str] = set() + self.selected_handles: Set[str] = set() root_person = db.get_person_from_gramps_id(self.list[0]) if root_person: self.init_ancestor_list(db, root_person) def reset(self): - self.map.clear() - self.map2.clear() + self.cache.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map2 + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles - def init_ancestor_list(self, db, person): - fam_id = person.get_main_parents_family_handle() + def init_ancestor_list(self, db: Database, person: Person): + fam_id = ( + person.parent_family_list[0] if len(person.parent_family_list) > 0 else None + ) if fam_id: fam = db.get_family_from_handle(fam_id) if fam: - f_id = fam.get_father_handle() - m_id = fam.get_mother_handle() + f_id = fam.father_handle + m_id = fam.mother_handle if m_id: self.go_deeper(db, db.get_person_from_handle(m_id)) if f_id: self.go_deeper(db, db.get_person_from_handle(f_id)) - def go_deeper(self, db, person): - if person and person.handle in self.map: - self.map2.add((person.handle)) + def go_deeper(self, db: Database, person: Person): + if person and person.handle in self.cache: + self.selected_handles.add((person.handle)) # the following keeps from scanning same parts of tree multiple # times and avoids crash on tree loops. return - self.map.add((person.handle)) + self.cache.add(person.handle) self.init_ancestor_list(db, person) diff --git a/gramps/gen/filters/rules/person/_isfemale.py b/gramps/gen/filters/rules/person/_isfemale.py index 2fa21e6e004..d59a6d20381 100644 --- a/gramps/gen/filters/rules/person/_isfemale.py +++ b/gramps/gen/filters/rules/person/_isfemale.py @@ -36,6 +36,14 @@ from ....lib.person import Person +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....db import Database + + # ------------------------------------------------------------------------- # # IsFemale @@ -48,5 +56,5 @@ class IsFemale(Rule): category = _("General filters") description = _("Matches all females") - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: return person.gender == Person.FEMALE diff --git a/gramps/gen/filters/rules/person/_islessthannthgenerationancestorof.py b/gramps/gen/filters/rules/person/_islessthannthgenerationancestorof.py index c88f8c332fa..f625bd5d105 100644 --- a/gramps/gen/filters/rules/person/_islessthannthgenerationancestorof.py +++ b/gramps/gen/filters/rules/person/_islessthannthgenerationancestorof.py @@ -35,6 +35,17 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import List, Set, Tuple +from ....lib import Person +from ....db import Database +from ....types import PersonHandle + + # ------------------------------------------------------------------------- # # IsLessThanNthGenerationAncestorOf @@ -52,32 +63,36 @@ class IsLessThanNthGenerationAncestorOf(Rule): "of a specified person not more than N generations away" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[str] = set() person = db.get_person_from_gramps_id(self.list[0]) if person: - root_handle = person.get_handle() + root_handle = person.handle if root_handle: self.init_ancestor_list(root_handle) - def init_ancestor_list(self, root_handle): - queue = [(root_handle, 1)] # generation 1 is root + def init_ancestor_list(self, root_handle: PersonHandle): + queue: List[Tuple[PersonHandle, int]] = [ + (root_handle, 1) + ] # generation 1 is root while queue: - handle, gen = queue.pop(0) # pop off front of queue - if handle in self.map: + handle, gen = queue.pop(0) + if handle in self.selected_handles: # if we have been here before, skip continue - self.map.add(handle) + self.selected_handles.add(handle) gen += 1 if gen <= int(self.list[1]): p = self.db.get_person_from_handle(handle) - fam_id = p.get_main_parents_family_handle() + fam_id = ( + p.parent_family_list[0] if len(p.parent_family_list) > 0 else None + ) if fam_id: fam = self.db.get_family_from_handle(fam_id) if fam: - f_id = fam.get_father_handle() - m_id = fam.get_mother_handle() + f_id = fam.father_handle + m_id = fam.mother_handle # append to back of queue: if f_id: queue.append((f_id, gen)) @@ -85,7 +100,7 @@ def init_ancestor_list(self, root_handle): queue.append((m_id, gen)) def reset(self): - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles diff --git a/gramps/gen/filters/rules/person/_islessthannthgenerationancestorofbookmarked.py b/gramps/gen/filters/rules/person/_islessthannthgenerationancestorofbookmarked.py index bbdda54b480..91e5b6b0e20 100644 --- a/gramps/gen/filters/rules/person/_islessthannthgenerationancestorofbookmarked.py +++ b/gramps/gen/filters/rules/person/_islessthannthgenerationancestorofbookmarked.py @@ -40,6 +40,17 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import List, Set +from ....lib import Person +from ....db import Database +from ....types import PersonHandle + + # ------------------------------------------------------------------------- # # IsLessThanNthGenerationAncestorOfBookmarked @@ -58,45 +69,40 @@ class IsLessThanNthGenerationAncestorOfBookmarked(Rule): "not more than N generations away" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - bookmarks = db.get_bookmarks().get() - self.map = set() - if len(bookmarks) == 0: - self.apply = lambda db, p: False - else: - self.bookmarks = set(bookmarks) - self.apply = self.apply_real + bookmarks: List[str] = db.get_bookmarks().get() + self.selected_handles: Set[PersonHandle] = set() + if len(bookmarks) != 0: + self.bookmarks: Set[PersonHandle] = set(bookmarks) for self.bookmarkhandle in self.bookmarks: self.init_ancestor_list(self.bookmarkhandle, 1) - def init_ancestor_list(self, handle, gen): - # if p.get_handle() in self.map: - # loop_error(self.orig,p) - if not handle or handle in self.map: + def init_ancestor_list(self, handle: PersonHandle, gen: int): + if not handle or handle in self.selected_handles: # if been here already, skip return if gen: - self.map.add(handle) + self.selected_handles.add(handle) if gen >= int(self.list[0]): return p = self.db.get_person_from_handle(handle) - fam_id = p.get_main_parents_family_handle() + fam_id = p.parent_family_list[0] if len(p.parent_family_list) > 0 else None if not fam_id: return fam = self.db.get_family_from_handle(fam_id) if fam: - f_id = fam.get_father_handle() - m_id = fam.get_mother_handle() + f_id = fam.father_handle + m_id = fam.mother_handle if f_id: self.init_ancestor_list(f_id, gen + 1) if m_id: self.init_ancestor_list(m_id, gen + 1) - def apply_real(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles def reset(self): - self.map.clear() + self.selected_handles.clear() diff --git a/gramps/gen/filters/rules/person/_islessthannthgenerationancestorofdefaultperson.py b/gramps/gen/filters/rules/person/_islessthannthgenerationancestorofdefaultperson.py index 8afef235388..d070690eabb 100644 --- a/gramps/gen/filters/rules/person/_islessthannthgenerationancestorofdefaultperson.py +++ b/gramps/gen/filters/rules/person/_islessthannthgenerationancestorofdefaultperson.py @@ -35,6 +35,17 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database +from ....types import PersonHandle + + # ------------------------------------------------------------------------- # # IsLessThanNthGenerationAncestorOfDefaultPerson @@ -52,44 +63,38 @@ class IsLessThanNthGenerationAncestorOfDefaultPerson(Rule): "Matches ancestors of the Home Person " "not more than N generations away" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() - p = db.get_default_person() + self.selected_handles: Set[PersonHandle] = set() + p: Person = db.get_default_person() if p: - self.def_handle = p.get_handle() - self.apply = self.apply_real - self.init_ancestor_list(self.def_handle, 1) - else: - self.apply = lambda db, p: False + self.init_ancestor_list(p.handle, 1) - def init_ancestor_list(self, handle, gen): - # if p.get_handle() in self.map: - # loop_error(self.orig,p) - if not handle or handle in self.map: + def init_ancestor_list(self, handle: PersonHandle, gen: int): + if not handle or handle in self.selected_handles: # if we have been here before, skip return if gen: - self.map.add(handle) + self.selected_handles.add(handle) if gen >= int(self.list[0]): return p = self.db.get_person_from_handle(handle) - fam_id = p.get_main_parents_family_handle() + fam_id = p.parent_family_list[0] if len(p.parent_family_list) > 0 else None if not fam_id: return fam = self.db.get_family_from_handle(fam_id) if fam: - f_id = fam.get_father_handle() - m_id = fam.get_mother_handle() + f_id = fam.father_handle + m_id = fam.mother_handle if f_id: self.init_ancestor_list(f_id, gen + 1) if m_id: self.init_ancestor_list(m_id, gen + 1) - def apply_real(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles def reset(self): - self.map.clear() + self.selected_handles.clear() diff --git a/gramps/gen/filters/rules/person/_islessthannthgenerationdescendantof.py b/gramps/gen/filters/rules/person/_islessthannthgenerationdescendantof.py index d6429edbc0e..67da4ebfb0a 100644 --- a/gramps/gen/filters/rules/person/_islessthannthgenerationdescendantof.py +++ b/gramps/gen/filters/rules/person/_islessthannthgenerationdescendantof.py @@ -35,6 +35,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsLessThanNthGenerationDescendantOf @@ -52,9 +62,9 @@ class IsLessThanNthGenerationDescendantOf(Rule): "specified person not more than N generations away" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[str] = set() try: root_person = db.get_person_from_gramps_id(self.list[0]) self.init_list(root_person, 0) @@ -62,24 +72,24 @@ def prepare(self, db, user): pass def reset(self): - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles - def init_list(self, person, gen): - if not person or person.handle in self.map: + def init_list(self, person: Person, gen: int): + if not person or person.handle in self.selected_handles: # if we have been here before, skip return if gen: - self.map.add(person.handle) + self.selected_handles.add(person.handle) if gen >= int(self.list[1]): return - for fam_id in person.get_family_handle_list(): + for fam_id in person.family_list: fam = self.db.get_family_from_handle(fam_id) if fam: - for child_ref in fam.get_child_ref_list(): + for child_ref in fam.child_ref_list: self.init_list( self.db.get_person_from_handle(child_ref.ref), gen + 1 ) diff --git a/gramps/gen/filters/rules/person/_ismale.py b/gramps/gen/filters/rules/person/_ismale.py index 1349ea18c9a..839fe4f0d93 100644 --- a/gramps/gen/filters/rules/person/_ismale.py +++ b/gramps/gen/filters/rules/person/_ismale.py @@ -36,6 +36,14 @@ from ....lib.person import Person +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....db import Database + + # ------------------------------------------------------------------------- # # IsMale @@ -48,5 +56,5 @@ class IsMale(Rule): category = _("General filters") description = _("Matches all males") - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: return person.gender == Person.MALE diff --git a/gramps/gen/filters/rules/person/_ismorethannthgenerationancestorof.py b/gramps/gen/filters/rules/person/_ismorethannthgenerationancestorof.py index 0391d9bafba..63e8368b61d 100644 --- a/gramps/gen/filters/rules/person/_ismorethannthgenerationancestorof.py +++ b/gramps/gen/filters/rules/person/_ismorethannthgenerationancestorof.py @@ -35,6 +35,17 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database +from ....types import PersonHandle + + # ------------------------------------------------------------------------- # # IsMoreThanNthGenerationAncestorOf @@ -52,29 +63,29 @@ class IsMoreThanNthGenerationAncestorOf(Rule): "of a specified person at least N generations away" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[PersonHandle] = set() person = db.get_person_from_gramps_id(self.list[0]) if person: - root_handle = person.get_handle() + root_handle = person.handle if root_handle: self.init_ancestor_list(root_handle) - def init_ancestor_list(self, root_handle): + def init_ancestor_list(self, root_handle: PersonHandle): queue = [(root_handle, 1)] # generation 1 is root while queue: handle, gen = queue.pop(0) # pop off front of queue if gen > int(self.list[1]): - self.map.add(handle) + self.selected_handles.add(handle) gen += 1 p = self.db.get_person_from_handle(handle) - fam_id = p.get_main_parents_family_handle() + fam_id = p.parent_family_list[0] if len(p.parent_family_list) > 0 else None if fam_id: fam = self.db.get_family_from_handle(fam_id) if fam: - f_id = fam.get_father_handle() - m_id = fam.get_mother_handle() + f_id = fam.father_handle + m_id = fam.mother_handle # append to back of queue: if f_id: queue.append((f_id, gen)) @@ -82,7 +93,7 @@ def init_ancestor_list(self, root_handle): queue.append((m_id, gen)) def reset(self): - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db, person: Person) -> bool: + return person.handle in self.selected_handles diff --git a/gramps/gen/filters/rules/person/_ismorethannthgenerationdescendantof.py b/gramps/gen/filters/rules/person/_ismorethannthgenerationdescendantof.py index 6869df63142..6a27551af6b 100644 --- a/gramps/gen/filters/rules/person/_ismorethannthgenerationdescendantof.py +++ b/gramps/gen/filters/rules/person/_ismorethannthgenerationdescendantof.py @@ -35,6 +35,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsMoreThanNthGenerationDescendantOf @@ -52,31 +62,31 @@ class IsMoreThanNthGenerationDescendantOf(Rule): "person at least N generations away" ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[str] = set() try: - root_person = db.get_person_from_gramps_id(self.list[0]) + root_person = db._get_raw_person_from_id_data(self.list[0]) self.init_list(root_person, 0) except: pass def reset(self): - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles - def init_list(self, person, gen): + def init_list(self, person: Person, gen: int) -> None: if not person: return if gen >= int(self.list[1]): - self.map.add(person.handle) + self.selected_handles.add(person.handle) - for fam_id in person.get_family_handle_list(): + for fam_id in person.family_list: fam = self.db.get_family_from_handle(fam_id) if fam: - for child_ref in fam.get_child_ref_list(): + for child_ref in fam.child_ref_list: self.init_list( self.db.get_person_from_handle(child_ref.ref), gen + 1 ) diff --git a/gramps/gen/filters/rules/person/_isparentoffiltermatch.py b/gramps/gen/filters/rules/person/_isparentoffiltermatch.py index a262ac23e31..5c692e9e238 100644 --- a/gramps/gen/filters/rules/person/_isparentoffiltermatch.py +++ b/gramps/gen/filters/rules/person/_isparentoffiltermatch.py @@ -36,6 +36,16 @@ from ._matchesfilter import MatchesFilter +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsParentOfFilterMatch @@ -50,9 +60,9 @@ class IsParentOfFilterMatch(Rule): category = _("Family filters") description = _("Matches parents of anybody matched by a filter") - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[str] = set() self.filt = MatchesFilter(self.list) self.filt.requestprepare(db, user) if user: @@ -64,24 +74,24 @@ def prepare(self, db, user): for person in db.iter_people(): if user: user.step_progress() - if self.filt.apply(db, person): + if self.filt.apply_to_one(db, person): self.init_list(person) if user: user.end_progress() def reset(self): self.filt.requestreset() - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db, person: Person) -> bool: + return person.handle in self.selected_handles - def init_list(self, person): - for fam_id in person.get_parent_family_handle_list(): + def init_list(self, person: Person): + for fam_id in person.parent_family_list: fam = self.db.get_family_from_handle(fam_id) if fam: - self.map.update( + self.selected_handles.update( parent_id - for parent_id in [fam.get_father_handle(), fam.get_mother_handle()] + for parent_id in [fam.father_handle, fam.mother_handle] if parent_id ) diff --git a/gramps/gen/filters/rules/person/_isrelatedwith.py b/gramps/gen/filters/rules/person/_isrelatedwith.py index c478257ff43..4d3de7a2179 100644 --- a/gramps/gen/filters/rules/person/_isrelatedwith.py +++ b/gramps/gen/filters/rules/person/_isrelatedwith.py @@ -35,6 +35,16 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import List, Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # RelatedWith @@ -48,64 +58,62 @@ class IsRelatedWith(Rule): category = _("Relationship filters") description = _("Matches people related to a specified person") - def prepare(self, db, user): + def prepare(self, db: Database, user): """prepare so the rule can be executed efficiently we build the list of people related to here, so that apply is only a check into this list """ self.db = db - self.relatives = [] + self.selected_handles: Set[str] = set() self.add_relative(db.get_person_from_gramps_id(self.list[0])) def reset(self): - self.relatives = [] + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.relatives + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles - def add_relative(self, start): - """Non-recursive function that scans relatives and add them to self.relatives""" + def add_relative(self, start: Person): + """Non-recursive function that scans relatives and add them to self.selected_handles""" if not (start): return - expand = [start] - relatives = {} + queue: List[Person] = [start] - while expand: - person = expand.pop() + while queue: + person = queue.pop() # Add the relative to the list - if person is None or (person.handle in relatives): + if person is None or (person.handle in self.selected_handles): continue - relatives[person.handle] = True + self.selected_handles.add(person.handle) - for family_handle in person.get_parent_family_handle_list(): + for family_handle in person.parent_family_list: family = self.db.get_family_from_handle(family_handle) if family: # Check Parents for parent_handle in ( - family.get_father_handle(), - family.get_mother_handle(), + family.father_handle, + family.mother_handle, ): if parent_handle: - expand.append(self.db.get_person_from_handle(parent_handle)) + queue.append(self.db.get_person_from_handle(parent_handle)) # Check Sibilings - for child_ref in family.get_child_ref_list(): - expand.append(self.db.get_person_from_handle(child_ref.ref)) + for child_ref in family.child_ref_list: + queue.append(self.db.get_person_from_handle(child_ref.ref)) - for family_handle in person.get_family_handle_list(): + for family_handle in person.family_list: family = self.db.get_family_from_handle(family_handle) if family: # Check Spouse for parent_handle in ( - family.get_father_handle(), - family.get_mother_handle(), + family.father_handle, + family.mother_handle, ): if parent_handle: - expand.append(self.db.get_person_from_handle(parent_handle)) + queue.append(self.db.get_person_from_handle(parent_handle)) # Check Children - for child_ref in family.get_child_ref_list(): - expand.append(self.db.get_person_from_handle(child_ref.ref)) + for child_ref in family.child_ref_list: + queue.append(self.db.get_person_from_handle(child_ref.ref)) - self.relatives = list(relatives.keys()) return diff --git a/gramps/gen/filters/rules/person/_issiblingoffiltermatch.py b/gramps/gen/filters/rules/person/_issiblingoffiltermatch.py index d6974d2d0fd..91f5701f7ac 100644 --- a/gramps/gen/filters/rules/person/_issiblingoffiltermatch.py +++ b/gramps/gen/filters/rules/person/_issiblingoffiltermatch.py @@ -36,6 +36,16 @@ from ._matchesfilter import MatchesFilter +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Set +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsSiblingOfFilterMatch @@ -49,9 +59,9 @@ class IsSiblingOfFilterMatch(Rule): category = _("Family filters") description = _("Matches siblings of anybody matched by a filter") - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[str] = set() self.matchfilt = MatchesFilter(self.list) self.matchfilt.requestprepare(db, user) if user: @@ -63,27 +73,29 @@ def prepare(self, db, user): for person in db.iter_people(): if user: user.step_progress() - if self.matchfilt.apply(db, person): + if self.matchfilt.apply_to_one(db, person): self.init_list(person) if user: user.end_progress() def reset(self): self.matchfilt.requestreset() - self.map.clear() + self.selected_handles.clear() - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles - def init_list(self, person): + def init_list(self, person: Person): if not person: return - fam_id = person.get_main_parents_family_handle() + fam_id = ( + person.parent_family_list[0] if len(person.parent_family_list) > 0 else None + ) if fam_id: fam = self.db.get_family_from_handle(fam_id) if fam: - self.map.update( + self.selected_handles.update( child_ref.ref - for child_ref in fam.get_child_ref_list() + for child_ref in fam.child_ref_list if child_ref and child_ref.ref != person.handle ) diff --git a/gramps/gen/filters/rules/person/_isspouseoffiltermatch.py b/gramps/gen/filters/rules/person/_isspouseoffiltermatch.py index c96f88a6acd..25fcac543fa 100644 --- a/gramps/gen/filters/rules/person/_isspouseoffiltermatch.py +++ b/gramps/gen/filters/rules/person/_isspouseoffiltermatch.py @@ -36,6 +36,15 @@ from ._matchesfilter import MatchesFilter +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # IsSpouseOfFilterMatch @@ -50,23 +59,23 @@ class IsSpouseOfFilterMatch(Rule): description = _("Matches people married to anybody matching a filter") category = _("Family filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): self.filt = MatchesFilter(self.list) self.filt.requestprepare(db, user) - def apply(self, db, person): - for family_handle in person.get_family_handle_list(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for family_handle in person.family_list: family = db.get_family_from_handle(family_handle) if family: for spouse_id in [ - family.get_father_handle(), - family.get_mother_handle(), + family.father_handle, + family.mother_handle, ]: if not spouse_id: continue if spouse_id == person.handle: continue - if self.filt.apply(db, db.get_person_from_handle(spouse_id)): + if self.filt.apply_to_one(db, db.get_person_from_handle(spouse_id)): return True return False diff --git a/gramps/gen/filters/rules/person/_iswitness.py b/gramps/gen/filters/rules/person/_iswitness.py index a7d755c281e..188fa63bf3a 100644 --- a/gramps/gen/filters/rules/person/_iswitness.py +++ b/gramps/gen/filters/rules/person/_iswitness.py @@ -32,6 +32,15 @@ from ....lib.eventtype import EventType from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + _ = glocale.translation.gettext @@ -54,7 +63,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.event_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things only want to do once. """ @@ -62,12 +71,12 @@ def prepare(self, db, user): self.event_type = EventType() self.event_type.set_from_xml_str(self.list[0]) - def apply(self, db, obj): + def apply_to_one(self, db: Database, obj: Person) -> bool: """ Apply the rule. Return True on a match. """ for event_ref in obj.event_ref_list: - if event_ref.role == EventRoleType.WITNESS: + if event_ref.role.value == EventRoleType.WITNESS: # This is the witness. # If event type was given, then check it. if self.event_type: diff --git a/gramps/gen/filters/rules/person/_matchidof.py b/gramps/gen/filters/rules/person/_matchidof.py index 52aca2709ca..9b34587a5dd 100644 --- a/gramps/gen/filters/rules/person/_matchidof.py +++ b/gramps/gen/filters/rules/person/_matchidof.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasIdOf @@ -48,5 +57,5 @@ class MatchIdOf(Rule): description = _("Matches person with a specified Gramps ID") category = _("General filters") - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Person) -> bool: return person.gramps_id.find(self.list[0]) != -1 diff --git a/gramps/gen/filters/rules/person/_missingparent.py b/gramps/gen/filters/rules/person/_missingparent.py index 2ee7aea8530..33f53072a48 100644 --- a/gramps/gen/filters/rules/person/_missingparent.py +++ b/gramps/gen/filters/rules/person/_missingparent.py @@ -36,6 +36,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # "People with less than 2 parents" # ------------------------------------------------------------------------- @@ -50,15 +59,15 @@ class MissingParent(Rule): ) category = _("Family filters") - def apply(self, db, person): - families = person.get_parent_family_handle_list() + def apply_to_one(self, db: Database, person: Person) -> bool: + families = person.parent_family_list if families == []: return True - for family_handle in person.get_parent_family_handle_list(): + for family_handle in families: family = db.get_family_from_handle(family_handle) if family: - father_handle = family.get_father_handle() - mother_handle = family.get_mother_handle() + father_handle = family.father_handle + mother_handle = family.mother_handle if not father_handle: return True if not mother_handle: diff --git a/gramps/gen/filters/rules/person/_multiplemarriages.py b/gramps/gen/filters/rules/person/_multiplemarriages.py index 30532b2d504..49778487dff 100644 --- a/gramps/gen/filters/rules/person/_multiplemarriages.py +++ b/gramps/gen/filters/rules/person/_multiplemarriages.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # "People with multiple marriage records" # ------------------------------------------------------------------------- @@ -45,5 +54,5 @@ class MultipleMarriages(Rule): description = _("Matches people who have more than one spouse") category = _("Family filters") - def apply(self, db, person): - return len(person.get_family_handle_list()) > 1 + def apply_to_one(self, db: Database, person: Person) -> bool: + return len(person.family_list) > 1 diff --git a/gramps/gen/filters/rules/person/_nevermarried.py b/gramps/gen/filters/rules/person/_nevermarried.py index 66a9634d2c8..207383909f6 100644 --- a/gramps/gen/filters/rules/person/_nevermarried.py +++ b/gramps/gen/filters/rules/person/_nevermarried.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # "People with no marriage records" # ------------------------------------------------------------------------- @@ -45,5 +54,5 @@ class NeverMarried(Rule): description = _("Matches people who have no spouse") category = _("Family filters") - def apply(self, db, person): - return len(person.get_family_handle_list()) == 0 + def apply_to_one(self, db: Database, person: Person) -> bool: + return len(person.family_list) == 0 diff --git a/gramps/gen/filters/rules/person/_nobirthdate.py b/gramps/gen/filters/rules/person/_nobirthdate.py index 22e979a15b5..e368f1dca64 100644 --- a/gramps/gen/filters/rules/person/_nobirthdate.py +++ b/gramps/gen/filters/rules/person/_nobirthdate.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # "People without a birth date" # ------------------------------------------------------------------------- @@ -45,15 +54,18 @@ class NoBirthdate(Rule): description = _("Matches people without a known birthdate") category = _("General filters") - def apply(self, db, person): - birth_ref = person.get_birth_ref() - if not birth_ref: - return True - birth = db.get_event_from_handle(birth_ref.ref) - if birth: - birth_obj = birth.get_date_object() - if not birth_obj: + def apply_to_one(self, db: Database, person: Person) -> bool: + if 0 <= person.birth_ref_index < len(person.event_ref_list): + birth_ref = person.event_ref_list[person.birth_ref_index] + if not birth_ref: return True - if birth_obj.sortval == 0: - return True - return False + birth = db.get_event_from_handle(birth_ref.ref) + if birth: + birth_obj = birth.date + if not birth_obj: + return True + if birth_obj.sortval == 0: + return True + return False + else: + return True diff --git a/gramps/gen/filters/rules/person/_nodeathdate.py b/gramps/gen/filters/rules/person/_nodeathdate.py index 6884d69d9e4..8429f259878 100644 --- a/gramps/gen/filters/rules/person/_nodeathdate.py +++ b/gramps/gen/filters/rules/person/_nodeathdate.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # "People without a death date" # ------------------------------------------------------------------------- @@ -45,15 +54,18 @@ class NoDeathdate(Rule): description = _("Matches people without a known deathdate") category = _("General filters") - def apply(self, db, person): - death_ref = person.get_death_ref() - if not death_ref: - return True - death = db.get_event_from_handle(death_ref.ref) - if death: - death_obj = death.get_date_object() - if not death_obj: + def apply_to_one(self, db: Database, person: Person) -> bool: + if 0 <= person.death_ref_index < len(person.event_ref_list): + death_ref = person.event_ref_list[person.death_ref_index] + if not death_ref: return True - if death_obj.sortval == 0: - return True - return False + death = db.get_event_from_handle(death_ref.ref) + if death: + death_obj = death.date + if not death_obj: + return True + if death_obj.sortval == 0: + return True + return False + else: + return True diff --git a/gramps/gen/filters/rules/person/_personwithincompleteevent.py b/gramps/gen/filters/rules/person/_personwithincompleteevent.py index 58ce20d7b31..81cb741a560 100644 --- a/gramps/gen/filters/rules/person/_personwithincompleteevent.py +++ b/gramps/gen/filters/rules/person/_personwithincompleteevent.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # "People with incomplete events" # ------------------------------------------------------------------------- @@ -45,12 +54,12 @@ class PersonWithIncompleteEvent(Rule): description = _("Matches people with missing date or place in an event") category = _("Event filters") - def apply(self, db, person): - for event_ref in person.get_event_ref_list(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for event_ref in person.event_ref_list: if event_ref: event = db.get_event_from_handle(event_ref.ref) - if not event.get_place_handle(): + if not event.place: return True - if not event.get_date_object(): + if not event.date: return True return False diff --git a/gramps/gen/filters/rules/person/_probablyalive.py b/gramps/gen/filters/rules/person/_probablyalive.py index 91d9e80b690..d368b69a8ae 100644 --- a/gramps/gen/filters/rules/person/_probablyalive.py +++ b/gramps/gen/filters/rules/person/_probablyalive.py @@ -37,6 +37,15 @@ from ....datehandler import parser +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # "People probably alive" # ------------------------------------------------------------------------- @@ -48,11 +57,11 @@ class ProbablyAlive(Rule): description = _("Matches people without indications of death that are not too old") category = _("General filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): try: self.current_date = parser.parse(str(self.list[0])) except: self.current_date = None - def apply(self, db, person): + def apply_to_one(self, db, person: Person) -> bool: return probably_alive(person, db, self.current_date) diff --git a/gramps/gen/filters/rules/person/_regexpname.py b/gramps/gen/filters/rules/person/_regexpname.py index 95aa6dc2740..ed522a6270c 100644 --- a/gramps/gen/filters/rules/person/_regexpname.py +++ b/gramps/gen/filters/rules/person/_regexpname.py @@ -36,6 +36,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Person +from ....db import Database + + # ------------------------------------------------------------------------- # # HasNameOf @@ -53,16 +62,16 @@ class RegExpName(Rule): category = _("General filters") allow_regex = True - def apply(self, db, person): - for name in [person.get_primary_name()] + person.get_alternate_names(): + def apply_to_one(self, db: Database, person: Person) -> bool: + for name in [person.primary_name] + person.alternate_names: for field in [ name.first_name, - name.get_surname(), name.suffix, name.title, name.nick, name.famnick, name.call, + name.get_surname(), ]: if self.match_substring(0, field): return True diff --git a/gramps/gen/filters/rules/person/_relationshippathbetween.py b/gramps/gen/filters/rules/person/_relationshippathbetween.py index 39b786afd45..51a83e1cb6d 100644 --- a/gramps/gen/filters/rules/person/_relationshippathbetween.py +++ b/gramps/gen/filters/rules/person/_relationshippathbetween.py @@ -35,6 +35,17 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import List, Set, Dict +from ....lib import Person +from ....db import Database +from ....types import PersonHandle + + # ------------------------------------------------------------------------- # # RelationshipPathBetween @@ -53,63 +64,71 @@ class RelationshipPathBetween(Rule): "path between two persons." ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[PersonHandle] = set() try: - root1_handle = db.get_person_from_gramps_id(self.list[0]).get_handle() - root2_handle = db.get_person_from_gramps_id(self.list[1]).get_handle() + root1_handle = db.get_person_from_gramps_id(self.list[0]).handle + root2_handle = db.get_person_from_gramps_id(self.list[1]).handle self.init_list(root1_handle, root2_handle) except: pass def reset(self): - self.map = () + self.selected_handles.clear() - def desc_list(self, handle, map, first): + def desc_list(self, handle: PersonHandle, map, first: bool): if not first: map.add(handle) p = self.db.get_person_from_handle(handle) - for fam_id in p.get_family_handle_list(): + for fam_id in p.family_list: fam = self.db.get_family_from_handle(fam_id) if fam: - for child_ref in fam.get_child_ref_list(): + for child_ref in fam.child_ref_list: if child_ref.ref: - self.desc_list(child_ref.ref, map, 0) - - def apply_filter(self, rank, handle, plist, pmap): + self.desc_list(child_ref.ref, map, False) + + def apply_filter( + self, + rank: int, + handle: PersonHandle, + plist: Set[PersonHandle], + pmap: Dict[PersonHandle, int], + ): if not handle: return person = self.db.get_person_from_handle(handle) if person is None: return plist.add(handle) - pmap[person.get_handle()] = rank + pmap[person.handle] = rank - fam_id = person.get_main_parents_family_handle() + fam_id = ( + person.parent_family_list[0] if len(person.parent_family_list) > 0 else None + ) if not fam_id: return family = self.db.get_family_from_handle(fam_id) if family is not None: - self.apply_filter(rank + 1, family.get_father_handle(), plist, pmap) - self.apply_filter(rank + 1, family.get_mother_handle(), plist, pmap) - - def apply(self, db, person): - return person.handle in self.map - - def init_list(self, p1_handle, p2_handle): - firstMap = {} - firstList = set() - secondMap = {} - secondList = set() - common = [] + self.apply_filter(rank + 1, family.father_handle, plist, pmap) + self.apply_filter(rank + 1, family.mother_handle, plist, pmap) + + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles + + def init_list(self, p1_handle: PersonHandle, p2_handle: PersonHandle): + firstMap: Dict[PersonHandle, int] = {} + firstList: Set[PersonHandle] = set() + secondMap: Dict[PersonHandle, int] = {} + secondList: Set[PersonHandle] = set() + common: List[PersonHandle] = [] rank = 9999999 self.apply_filter(0, p1_handle, firstList, firstMap) self.apply_filter(0, p2_handle, secondList, secondMap) - for person_handle in firstList & secondList: + for person_handle in firstList and secondList: new_rank = firstMap[person_handle] if new_rank < rank: rank = new_rank @@ -121,8 +140,8 @@ def init_list(self, p1_handle, p2_handle): path2 = set([p2_handle]) for person_handle in common: - new_map = set() - self.desc_list(person_handle, new_map, 1) + new_map: Set[PersonHandle] = set() + self.desc_list(person_handle, new_map, True) path1.update(new_map.intersection(firstMap)) path2.update(new_map.intersection(secondMap)) - self.map.update(path1, path2, common) + self.selected_handles.update(path1, path2, common) diff --git a/gramps/gen/filters/rules/person/_relationshippathbetweenbookmarks.py b/gramps/gen/filters/rules/person/_relationshippathbetweenbookmarks.py index 9c4619523a3..8c6c5bd664a 100644 --- a/gramps/gen/filters/rules/person/_relationshippathbetweenbookmarks.py +++ b/gramps/gen/filters/rules/person/_relationshippathbetweenbookmarks.py @@ -38,6 +38,17 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import List, Set, Dict +from ....lib import Person +from ....db import Database +from ....types import PersonHandle, FamilyHandle + + # ------------------------------------------------------------------------- # # RelationshipPathBetween @@ -58,56 +69,57 @@ class RelationshipPathBetweenBookmarks(Rule): "path(s) between bookmarked persons." ) - def prepare(self, db, user): + def prepare(self, db: Database, user): self.db = db - self.map = set() + self.selected_handles: Set[PersonHandle] = set() bookmarks = db.get_bookmarks().get() - if len(bookmarks) == 0: - self.apply = lambda db, p: False - else: - self.bookmarks = set(bookmarks) + self.bookmarks: Set[PersonHandle] = set(bookmarks) try: self.init_list() except: pass def reset(self): - self.map.clear() + self.selected_handles.clear() # # Returns a name, given a handle. - def hnm(self, handle): + def hnm(self, handle: PersonHandle): try: person = self.db.get_person_from_handle(handle) except: return None if person is None: return None - try: - name = person.get_primary_name().get_name() - except: + + if person.primary_name: + return person.primary_name.name + else: return None - return name # # Given a group of individuals, returns all of their parents. # The value keyed by the individual handles is the path from # the original person up, like generation[gfather]= [son,father,gfather] - def parents(self, generation): + def parents(self, generation: Dict[PersonHandle, List[PersonHandle]]): if len(generation) < 1: return None - prev_generation = {} + prev_generation: Dict[PersonHandle, List[PersonHandle]] = {} for handle in generation: try: person = self.db.get_person_from_handle(handle) if person is None: continue - fam_id = person.get_main_parents_family_handle() - family = self.db.get_family_from_handle(fam_id) + fam_id = ( + person.parent_family_list[0] + if len(person.parent_family_list) > 0 + else None + ) + family = self.db.get_family_from_handle(fam_id) # type: ignore if family is None: continue - fhandle = family.get_father_handle() - mhandle = family.get_mother_handle() + fhandle = family.father_handle + mhandle = family.mother_handle if fhandle: prev_generation[fhandle] = generation[handle] + [fhandle] if mhandle: @@ -119,7 +131,7 @@ def parents(self, generation): # # Given two handles for individuals, a list of all individuals # in the relationship path between the two. - def rel_path_for_two(self, handle1, handle2): + def rel_path_for_two(self, handle1: PersonHandle, handle2: PersonHandle): # print "rel_path_for_two (", handle1, self.hnm(handle1), ",", handle2, self.hnm(handle2), ")" rel_path = {} # Result map gmap1 = {handle1: [handle1]} # Key is ancestor, value is the path @@ -149,11 +161,11 @@ def rel_path_for_two(self, handle1, handle2): # print " In rel_path_for_two, returning rel_path = ", rel_path return rel_path - def init_list(self): - self.map.update(self.bookmarks) + def init_list(self) -> None: + self.selected_handles.update(self.bookmarks) if len(self.bookmarks) < 2: return - bmarks = list(self.bookmarks) + bmarks: List[PersonHandle] = list(self.bookmarks) # Go through all bookmarked individuals, and mark all # of the people in each of the paths betweent them. @@ -162,9 +174,9 @@ def init_list(self): for j in range(i + 1, lb): try: pathmap = self.rel_path_for_two(bmarks[i], bmarks[j]) - self.map.update(pathmap) + self.selected_handles.update(pathmap) except: pass - def apply(self, db, person): - return person.handle in self.map + def apply_to_one(self, db: Database, person: Person) -> bool: + return person.handle in self.selected_handles diff --git a/gramps/gen/filters/rules/person/_searchname.py b/gramps/gen/filters/rules/person/_searchname.py index f6b62be8d5b..fde9ac05eec 100644 --- a/gramps/gen/filters/rules/person/_searchname.py +++ b/gramps/gen/filters/rules/person/_searchname.py @@ -36,6 +36,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Any +from ....db import Database + + # ------------------------------------------------------------------------- # # HasNameOf @@ -49,12 +58,12 @@ class SearchName(Rule): description = _("Matches people with a specified (partial) name") category = _("General filters") - def apply(self, db, person): + def apply_to_one(self, db: Database, person: Any) -> bool: src = self.list[0].upper() if not src: return False - for name in [person.get_primary_name()] + person.get_alternate_names(): + for name in [person.primary_name] + person.alternate_names: for field in [ name.first_name, name.get_surname(), diff --git a/gramps/gen/filters/rules/place/_hasdata.py b/gramps/gen/filters/rules/place/_hasdata.py index cf431af5ec5..12bda04950d 100644 --- a/gramps/gen/filters/rules/place/_hasdata.py +++ b/gramps/gen/filters/rules/place/_hasdata.py @@ -32,6 +32,15 @@ from ....lib import PlaceType from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Place +from ....db import Database + + _ = glocale.translation.sgettext @@ -59,7 +68,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.place_type = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -67,17 +76,17 @@ def prepare(self, db, user): self.place_type = PlaceType() self.place_type.set_from_xml_str(self.list[1]) - def apply(self, _db, obj): + def apply_to_one(self, _db: Database, obj: Place) -> bool: """ Apply the rule. Return True on a match. """ if not self.match_name(obj): return False - if self.place_type and obj.get_type() != self.place_type: + if self.place_type and obj.place_type != self.place_type: return False - if not self.match_substring(2, obj.get_code()): + if not self.match_substring(2, obj.code): return False return True @@ -87,6 +96,6 @@ def match_name(self, place): Match any name in a list of names. """ for name in place.get_all_names(): - if self.match_substring(0, name.get_value()): + if self.match_substring(0, name.value): return True return False diff --git a/gramps/gen/filters/rules/place/_hasnolatorlon.py b/gramps/gen/filters/rules/place/_hasnolatorlon.py index 768592d3c5a..fac33ddf20b 100644 --- a/gramps/gen/filters/rules/place/_hasnolatorlon.py +++ b/gramps/gen/filters/rules/place/_hasnolatorlon.py @@ -37,6 +37,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Place +from ....db import Database + + # ------------------------------------------------------------------------- # # HasNoLatOrLon @@ -50,7 +59,7 @@ class HasNoLatOrLon(Rule): description = _("Matches places with empty latitude or longitude") category = _("Position filters") - def apply(self, db, place): - if place.get_latitude().strip and place.get_longitude().strip(): + def apply_to_one(self, db: Database, place: Place) -> bool: + if place.lat.strip() and place.long.strip(): return False return True diff --git a/gramps/gen/filters/rules/place/_hasplace.py b/gramps/gen/filters/rules/place/_hasplace.py index 544a1cc8f2a..4a020e955e6 100644 --- a/gramps/gen/filters/rules/place/_hasplace.py +++ b/gramps/gen/filters/rules/place/_hasplace.py @@ -39,6 +39,15 @@ from ....utils.location import get_locations +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Place +from ....db import Database + + # ------------------------------------------------------------------------- # # HasPlace @@ -73,11 +82,11 @@ class HasPlace(Rule): PlaceType.PARISH: 8, } - def apply(self, db, place): - if not self.match_substring(0, place.get_title()): + def apply_to_one(self, db: Database, place: Place) -> bool: + if not self.match_substring(0, place.title): return False - if not self.match_substring(7, place.get_code()): + if not self.match_substring(7, place.code): return False # If no location data was given then we're done: match diff --git a/gramps/gen/filters/rules/place/_hastitle.py b/gramps/gen/filters/rules/place/_hastitle.py index 6820e480e81..9c013e3bc5c 100644 --- a/gramps/gen/filters/rules/place/_hastitle.py +++ b/gramps/gen/filters/rules/place/_hastitle.py @@ -37,6 +37,15 @@ from ....display.place import displayer +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Place +from ....db import Database + + # ------------------------------------------------------------------------- # # HasTitle @@ -53,7 +62,7 @@ class HasTitle(Rule): category = _("General filters") allow_regex = True - def apply(self, db, place): + def apply_to_one(self, db: Database, place: Place) -> bool: if not self.match_substring(0, displayer.display(db, place)): return False return True diff --git a/gramps/gen/filters/rules/place/_inlatlonneighborhood.py b/gramps/gen/filters/rules/place/_inlatlonneighborhood.py index c3c6bfa0b40..0ac9dd9806a 100644 --- a/gramps/gen/filters/rules/place/_inlatlonneighborhood.py +++ b/gramps/gen/filters/rules/place/_inlatlonneighborhood.py @@ -38,6 +38,16 @@ from ....utils.place import conv_lat_lon +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Union +from ....lib import Place +from ....db import Database + + # ------------------------------------------------------------------------- # # InLatLonNeighborhood @@ -61,7 +71,9 @@ class InLatLonNeighborhood(Rule): ) category = _("Position filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): + self.halfheight: Union[float, None] = None + self.halfwidth: Union[float, None] = None if self.list[0]: try: self.halfheight = float(self.list[2]) / 2.0 @@ -87,7 +99,8 @@ def prepare(self, db, user): self.list[1] = "0.0" # we allow a band instead of a triangle - self.lat, self.lon = conv_lat_lon(self.list[0], self.list[1], "D.D8") + results = conv_lat_lon(self.list[0], self.list[1], "D.D8") # type: ignore + self.lat, self.lon = results if isinstance(results, tuple) else (None, None) # type: ignore if self.lat is not None and self.lon is not None: self.lat = float(self.lat) self.lon = float(self.lon) @@ -131,7 +144,7 @@ def prepare(self, db, user): self.E2 = 180.0 self.W = -180 - def apply(self, db, place): + def apply_to_one(self, db: Database, place: Place) -> bool: if self.halfheight == -1 and self.halfwidth == -1: return False @@ -146,16 +159,17 @@ def apply(self, db, place): # now we know at least one is given in the filter and is valid # the place we look at must have lat AND lon entered - if not (place.get_latitude().strip and place.get_longitude().strip()): + if not (place.lat.strip() and place.long.strip()): return False - latpl, lonpl = conv_lat_lon(place.get_latitude(), place.get_longitude(), "D.D8") + results = conv_lat_lon(place.lat, place.long, "D.D8") # type: ignore + latpl, lonpl = results if isinstance(results, tuple) else (None, None) # type: ignore if latpl and lonpl: - latpl = float(latpl) - lonpl = float(lonpl) + latpl = float(latpl) # type: ignore + lonpl = float(lonpl) # type: ignore if self.halfheight != -1: # check lat - if latpl < self.N or latpl > self.S: + if latpl < self.N or latpl > self.S: # type: ignore return False if self.halfwidth != -1: @@ -163,11 +177,11 @@ def apply(self, db, place): # and must keep counting if self.doublesquares: # two squares to look in : - if (lonpl < self.W or lonpl > self.E) and ( - lonpl < self.W2 or lonpl > self.E2 - ): + left = lonpl < self.W or lonpl > self.E # type: ignore + right = lonpl < self.W2 or lonpl > self.E2 # type: ignore + if left and right: # type: ignore return False - elif lonpl < self.W or lonpl > self.E: + elif lonpl < self.W or lonpl > self.E: # type: ignore return False return True diff --git a/gramps/gen/filters/rules/place/_isenclosedby.py b/gramps/gen/filters/rules/place/_isenclosedby.py index 7be1b88465f..a222e3cbd73 100644 --- a/gramps/gen/filters/rules/place/_isenclosedby.py +++ b/gramps/gen/filters/rules/place/_isenclosedby.py @@ -36,6 +36,14 @@ from .. import Rule from ....utils.location import located_in +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Place +from ....db import Database + # ------------------------------------------------------------------------- # @@ -52,13 +60,13 @@ class IsEnclosedBy(Rule): description = _("Matches a place enclosed by a particular place") category = _("General filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): self.handle = None place = db.get_place_from_gramps_id(self.list[0]) if place: self.handle = place.handle - def apply(self, db, place): + def apply_to_one(self, db: Database, place: Place) -> bool: if self.handle is None: return False if self.list[1] == "1" and place.handle == self.handle: diff --git a/gramps/gen/filters/rules/place/_matcheseventfilter.py b/gramps/gen/filters/rules/place/_matcheseventfilter.py index b5a8745d250..507408cd4d0 100644 --- a/gramps/gen/filters/rules/place/_matcheseventfilter.py +++ b/gramps/gen/filters/rules/place/_matcheseventfilter.py @@ -35,6 +35,15 @@ from .._matchesfilterbase import MatchesFilterBase +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Event +from ....db import Database + + # ------------------------------------------------------------------------- # # MatchesFilter @@ -58,12 +67,11 @@ class MatchesEventFilter(MatchesFilterBase): # we want to have this filter show event filters namespace = "Event" - def apply(self, db, event): + def apply_to_one(self, db: Database, event: Event) -> bool: filt = self.find_filter() if filt: - for classname, handle in db.find_backlink_handles( - event.get_handle(), ["Event"] - ): - if filt.check(db, handle): + for classname, handle in db.find_backlink_handles(event.handle, ["Event"]): + data = db.method("get_%s_from_handle", classname)(handle) + if filt.apply_to_one(db, data): return True return False diff --git a/gramps/gen/filters/rules/place/_withinarea.py b/gramps/gen/filters/rules/place/_withinarea.py index 247d8f7e8a5..fbed486e072 100644 --- a/gramps/gen/filters/rules/place/_withinarea.py +++ b/gramps/gen/filters/rules/place/_withinarea.py @@ -38,11 +38,22 @@ # Gramps modules # # ------------------------------------------------------------------------- -from gramps.gen.errors import FilterError +from ....errors import FilterError from ....const import GRAMPS_LOCALE as glocale from .. import Rule from ....utils.place import conv_lat_lon + +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from typing import Union +from ....lib import Place +from ....db import Database + + _ = glocale.translation.sgettext @@ -61,24 +72,23 @@ class WithinArea(Rule): description = _("Matches places within a given distance of another place") category = _("Position filters") handle = None - radius = None - latitude = None - longitude = None + radius: float = 0.0 + latitude: Union[float, None] = None + longitude: Union[float, None] = None - def prepare(self, db, user): + def prepare(self, db: Database, user): ref_place = db.get_place_from_gramps_id(self.list[0]) self.handle = None - self.radius = None self.latitude = None self.longitude = None if ref_place: self.handle = ref_place.handle - latitude = ref_place.get_latitude() + latitude = ref_place.lat if latitude == "": latitude = None return - longitude = ref_place.get_longitude() - self.latitude, self.longitude = conv_lat_lon(latitude, longitude, "D.D8") + longitude = ref_place.long + self.latitude, self.longitude = conv_lat_lon(latitude, longitude, "D.D8") # type: ignore if self.latitude is None or self.longitude is None: raise FilterError( _("Cannot use the filter 'within area'"), @@ -106,16 +116,19 @@ def prepare(self, db, user): self.radius = float(value) self.radius = self.radius / 2 - def apply(self, dummy_db, place): + def apply_to_one(self, db: Database, place: Place) -> bool: + latit: Union[float, None] = None + longit: Union[float, None] = None + if not (place and self.handle and self.latitude and self.longitude): return False if place: - lat = place.get_latitude() - lon = place.get_longitude() + lat = place.lat + lon = place.long if lat and lon: - latit, longit = conv_lat_lon(lat, lon, "D.D8") + latit, longit = conv_lat_lon(lat, lon, "D.D8") # type: ignore return ( - latit + latit # type: ignore and longit and ( hypot( diff --git a/gramps/gen/filters/rules/repository/_hasrepo.py b/gramps/gen/filters/rules/repository/_hasrepo.py index e2afb39add6..cd834e5c190 100644 --- a/gramps/gen/filters/rules/repository/_hasrepo.py +++ b/gramps/gen/filters/rules/repository/_hasrepo.py @@ -31,6 +31,15 @@ from ....lib.repotype import RepositoryType from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Repository +from ....db import Database + + _ = glocale.translation.sgettext @@ -59,7 +68,7 @@ def __init__(self, arg, use_regex=False, use_case=False): super().__init__(arg, use_regex, use_case) self.rtype = None - def prepare(self, db, user): + def prepare(self, db: Database, user): """ Prepare the rule. Things we only want to do once. """ @@ -67,11 +76,11 @@ def prepare(self, db, user): self.rtype = RepositoryType() self.rtype.set_from_xml_str(self.list[1]) - def apply(self, _db, obj): + def apply_to_one(self, _db: Database, obj: Repository) -> bool: """ Apply the rule. Return True on a match. """ - if not self.match_substring(0, obj.get_name()): + if not self.match_substring(0, obj.name): return False if self.rtype: diff --git a/gramps/gen/filters/rules/repository/_matchesnamesubstringof.py b/gramps/gen/filters/rules/repository/_matchesnamesubstringof.py index 8ae45db63c4..523b13ab608 100644 --- a/gramps/gen/filters/rules/repository/_matchesnamesubstringof.py +++ b/gramps/gen/filters/rules/repository/_matchesnamesubstringof.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Repository +from ....db import Database + + # ------------------------------------------------------------------------- # "Repositories having a name that contain a substring" # ------------------------------------------------------------------------- @@ -47,6 +56,6 @@ class MatchesNameSubstringOf(Rule): category = _("General filters") allow_regex = True - def apply(self, db, repository): + def apply_to_one(self, db: Database, repository: Repository) -> bool: """Apply the filter""" - return self.match_substring(0, repository.get_name()) + return self.match_substring(0, repository.name) diff --git a/gramps/gen/filters/rules/source/_hasrepository.py b/gramps/gen/filters/rules/source/_hasrepository.py index e1c9c8e626a..0288c9b1141 100644 --- a/gramps/gen/filters/rules/source/_hasrepository.py +++ b/gramps/gen/filters/rules/source/_hasrepository.py @@ -29,8 +29,6 @@ # ------------------------------------------------------------------------- from ....const import GRAMPS_LOCALE as glocale -_ = glocale.translation.gettext - # ------------------------------------------------------------------------- # # Gramps modules @@ -39,6 +37,18 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Source +from ....db import Database + + +_ = glocale.translation.gettext + + # ------------------------------------------------------------------------- # "People who have images" # ------------------------------------------------------------------------- @@ -50,7 +60,7 @@ class HasRepository(Rule): description = _("Matches sources with a certain number of repository references") category = _("General filters") - def prepare(self, db, user): + def prepare(self, db: Database, user): # things we want to do just once, not for every handle if self.list[1] == "less than": self.count_type = 0 @@ -61,8 +71,8 @@ def prepare(self, db, user): self.userSelectedCount = int(self.list[0]) - def apply(self, db, obj): - count = len(obj.get_reporef_list()) + def apply_to_one(self, db, obj: Source) -> bool: + count = len(obj.reporef_list) if self.count_type == 0: # "less than" return count < self.userSelectedCount elif self.count_type == 2: # "greater than" diff --git a/gramps/gen/filters/rules/source/_hasrepositorycallnumberref.py b/gramps/gen/filters/rules/source/_hasrepositorycallnumberref.py index 090172cf654..d04e03563a0 100644 --- a/gramps/gen/filters/rules/source/_hasrepositorycallnumberref.py +++ b/gramps/gen/filters/rules/source/_hasrepositorycallnumberref.py @@ -26,6 +26,15 @@ # ------------------------------------------------------------------------- from ....const import GRAMPS_LOCALE as glocale +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Source +from ....db import Database + + _ = glocale.translation.gettext # ------------------------------------------------------------------------- @@ -51,8 +60,8 @@ class HasRepositoryCallNumberRef(Rule): category = _("General filters") allow_regex = True - def apply(self, db, obj): - for repo_ref in obj.get_reporef_list(): + def apply_to_one(self, db: Database, obj: Source) -> bool: + for repo_ref in obj.reporef_list: if self.match_substring(0, repo_ref.call_number): return True return False diff --git a/gramps/gen/filters/rules/source/_matchesrepositoryfilter.py b/gramps/gen/filters/rules/source/_matchesrepositoryfilter.py index 2c0e8f18222..60c46b6c6c5 100644 --- a/gramps/gen/filters/rules/source/_matchesrepositoryfilter.py +++ b/gramps/gen/filters/rules/source/_matchesrepositoryfilter.py @@ -26,6 +26,15 @@ # ------------------------------------------------------------------------- from ....const import GRAMPS_LOCALE as glocale +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Source +from ....db import Database + + _ = glocale.translation.gettext # ------------------------------------------------------------------------- @@ -53,17 +62,18 @@ class MatchesRepositoryFilter(MatchesFilterBase): # we want to have this filter show repository filters namespace = "Repository" - def prepare(self, db, user): + def prepare(self, db: Database, user): MatchesFilterBase.prepare(self, db, user) self.MRF_filt = self.find_filter() - def apply(self, db, object): + def apply_to_one(self, db: Database, object: Source) -> bool: if self.MRF_filt is None: return False - repolist = [x.ref for x in object.get_reporef_list()] + repolist = [x.ref for x in object.reporef_list] for repohandle in repolist: # check if repo in repository filter - if self.MRF_filt.check(db, repohandle): + repo = db.get_repository_from_handle(repohandle) + if self.MRF_filt.apply_to_one(db, repo): return True return False diff --git a/gramps/gen/filters/rules/source/_matchestitlesubstringof.py b/gramps/gen/filters/rules/source/_matchestitlesubstringof.py index e88e70c7362..ba247921d46 100644 --- a/gramps/gen/filters/rules/source/_matchestitlesubstringof.py +++ b/gramps/gen/filters/rules/source/_matchestitlesubstringof.py @@ -35,6 +35,15 @@ from .. import Rule +# ------------------------------------------------------------------------- +# +# Typing modules +# +# ------------------------------------------------------------------------- +from ....lib import Source +from ....db import Database + + # ------------------------------------------------------------------------- # "Sources having a title that contain a substring" # ------------------------------------------------------------------------- @@ -47,6 +56,6 @@ class MatchesTitleSubstringOf(Rule): category = _("General filters") allow_regex = True - def apply(self, db, source): + def apply_to_one(self, db: Database, source: Source) -> bool: """Apply the filter""" - return self.match_substring(0, source.get_title()) + return self.match_substring(0, source.title) diff --git a/gramps/gen/filters/rules/test/person_rules_test.py b/gramps/gen/filters/rules/test/person_rules_test.py index 52227c8ee22..c55f5559986 100644 --- a/gramps/gen/filters/rules/test/person_rules_test.py +++ b/gramps/gen/filters/rules/test/person_rules_test.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2016 Tom Samstag +# Copyright (C) 2025 Doug Blank # # 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 @@ -25,6 +26,7 @@ import os from time import perf_counter import inspect +from contextlib import contextmanager from ....filters import reload_custom_filters @@ -102,6 +104,37 @@ EXAMPLE = os.path.join(TEST_DIR, "example.gramps") +@contextmanager +def count_method_calls(cls, method_name): + """ + Context manager to monkey-patch a method and count its calls. + + Args: + cls: The class containing the method. + method_name: The name of the method to patch. + + Yields: + A callable that returns the call count. + """ + original_method = getattr(cls, method_name) + call_count = 0 + + def patched_method(self, *args, **kwargs): + nonlocal call_count + call_count += 1 + return original_method(self, *args, **kwargs) + + setattr(cls, method_name, patched_method) + + def get_call_count(): + return call_count + + try: + yield get_call_count + finally: + setattr(cls, method_name, original_method) + + class BaseTest(unittest.TestCase): """ Person rule tests. @@ -1089,6 +1122,34 @@ def test_isdefaultperson(self): ), ) + def test_isdefaultperson_optimized(self): + """ + Test IsDefaultPerson rule. + """ + with count_method_calls(IsDefaultPerson, "apply_to_one") as get_call_count: + rule = IsDefaultPerson([]) + self.assertEqual( + self.filter_with_rule(rule), + set( + [ + "GNUJQCL9MD64AM56OH", + ] + ), + ) + # This used the optimizer, so it didn't loop through DB + self.assertEqual(get_call_count(), 1) + + def test_isfemale_not_optimized(self): + """ + Test IsFemale rule. Same as below, but tests optimizer. + """ + with count_method_calls(IsFemale, "apply_to_one") as get_call_count: + rule = IsFemale([]) + # too many to list out to test explicitly + self.assertEqual(len(self.filter_with_rule(rule)), 940) + # This did not use the optimizer, so it did loop through DB + self.assertEqual(get_call_count(), self.db.get_number_of_people()) + def test_isfemale(self): """ Test IsFemale rule. diff --git a/gramps/gen/lib/json_utils.py b/gramps/gen/lib/json_utils.py index f8a85c54b81..51a67b496e6 100644 --- a/gramps/gen/lib/json_utils.py +++ b/gramps/gen/lib/json_utils.py @@ -100,6 +100,24 @@ def __getitem__(self, position): else: return value + def __iter__(self): + self.index = 0 + return self + + def __next__(self): + if self.index >= len(self): + raise StopIteration + + result = self[self.index] + self.index += 1 + return result + + def __add__(self, value): + return DataList([x for x in self] + [x for x in value]) + + def __radd__(self, value): + return DataList([x for x in self] + [x for x in value]) + def remove_object(data): """ diff --git a/gramps/gen/lib/serialize.py b/gramps/gen/lib/serialize.py index 29eb736b354..89352289c05 100644 --- a/gramps/gen/lib/serialize.py +++ b/gramps/gen/lib/serialize.py @@ -38,6 +38,7 @@ # Gramps modules # # ------------------------------------------------------------------------ +import gramps.gen.lib as lib from .json_utils import ( dict_to_string, data_to_object, diff --git a/gramps/gen/lib/surname.py b/gramps/gen/lib/surname.py index 84aff9e3e2d..577f9a472ec 100644 --- a/gramps/gen/lib/surname.py +++ b/gramps/gen/lib/surname.py @@ -69,6 +69,25 @@ def __init__(self, source=None, data=None): if data: self.unserialize(data) + def get_object_state(self): + """ + Get the current object state as a dictionary. + """ + attr_dict = dict( + (key, value) + for key, value in self.__dict__.items() + if not key.startswith("_") + ) + attr_dict["_class"] = self.__class__.__name__ + return attr_dict + + def set_object_state(self, attr_dict): + """ + Set the current object state using information provided in the given + dictionary. + """ + self.__dict__.update(attr_dict) + def serialize(self): """ Convert the object to a serialized tuple of data. diff --git a/gramps/gen/lib/test/datadict_test.py b/gramps/gen/lib/test/datadict_test.py index 8ce432ee4b7..fd5df9ab1bb 100644 --- a/gramps/gen/lib/test/datadict_test.py +++ b/gramps/gen/lib/test/datadict_test.py @@ -115,7 +115,5 @@ def test_append_value(self): self.assertIsInstance(dl[0], int) def test_combined_list(self): - # FIXME: dealt with in a later PR dl = DataList([]) - with self.assertRaises(AssertionError): - self.assertIsInstance(dl + [], DataList) + self.assertIsInstance(dl + [], DataList) diff --git a/gramps/gen/proxy/living.py b/gramps/gen/proxy/living.py index f38c22dd3c5..8afd2dc86c0 100644 --- a/gramps/gen/proxy/living.py +++ b/gramps/gen/proxy/living.py @@ -45,7 +45,6 @@ Note, Tag, ) -from ..utils.alive import probably_alive from ..config import config from ..const import GRAMPS_LOCALE as glocale @@ -256,6 +255,8 @@ def __is_living(self, person): Returns True if the person is considered living. Returns False if the person is not considered living. """ + from ..utils.alive import probably_alive + person_handle = person.get_handle() unfil_person = self.get_unfiltered_person(person_handle) return probably_alive( diff --git a/gramps/gen/utils/alive.py b/gramps/gen/utils/alive.py index b1a3773832d..48246c9c198 100644 --- a/gramps/gen/utils/alive.py +++ b/gramps/gen/utils/alive.py @@ -41,6 +41,7 @@ from ..display.name import displayer as name_displayer from ..lib.date import Date, Today from ..lib.person import Person +from ..lib.json_utils import DataDict from ..errors import DatabaseError from ..const import GRAMPS_LOCALE as glocale from ..proxy.proxybase import ProxyDbBase @@ -164,7 +165,7 @@ def get_person_bd(class_or_handle): explain_death, ) - if isinstance(class_or_handle, Person): + if isinstance(class_or_handle, (Person, DataDict)): thisperson = class_or_handle elif isinstance(class_or_handle, str): thisperson = self.db.get_person_from_handle(class_or_handle) diff --git a/gramps/gui/views/treemodels/placemodel.py b/gramps/gui/views/treemodels/placemodel.py index 4b7786d5ecc..0269180d7a5 100644 --- a/gramps/gui/views/treemodels/placemodel.py +++ b/gramps/gui/views/treemodels/placemodel.py @@ -138,7 +138,7 @@ def column_name(self, data): def search_name(self, data): """The search name includes all alt names to enable finding by alt name""" - return ",".join([data.name.value] + [name.value for name in data.alt_names]) + return ",".join([[data.name.value] + [name.value for name in data.alt_names]]) def column_longitude(self, data): if not data.long: diff --git a/gramps/plugins/db/dbapi/test/db_test.py b/gramps/plugins/db/dbapi/test/db_test.py index b9e42f22bf0..15618e4fe33 100644 --- a/gramps/plugins/db/dbapi/test/db_test.py +++ b/gramps/plugins/db/dbapi/test/db_test.py @@ -578,7 +578,10 @@ def __get_cursor_test(self, cursor_func, raw_func): with cursor_func() as cursor: for handle, data1 in cursor: data2 = raw_func(handle) - self.assertEqual(data1, data2) + if isinstance(data1, dict) and isinstance(data2, dict): + self.assertDictEqual(dict(data1), dict(data2)) + else: + self.assertEqual(data1, data2) def test_get_person_cursor(self): self.__get_cursor_test(self.db.get_person_cursor, self.db.get_raw_person_data) diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 3ca48867f97..8fa001cfe30 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -100,6 +100,7 @@ gramps/gen/filters/__init__.py gramps/gen/filters/_filterlist.py gramps/gen/filters/_paramfilter.py gramps/gen/filters/_searchfilter.py +gramps/gen/filters/optimizer.py # # gen.filters.rules package # @@ -117,7 +118,6 @@ gramps/gen/filters/rules/event/__init__.py # gen.filters.rules.family package # gramps/gen/filters/rules/family/__init__.py -gramps/gen/filters/rules/family/_memberbase.py # # gen.filters.rules.media package #