diff --git a/gramps_webapi/api/resources/filters.py b/gramps_webapi/api/resources/filters.py index 1567abad..5aea2612 100644 --- a/gramps_webapi/api/resources/filters.py +++ b/gramps_webapi/api/resources/filters.py @@ -2,6 +2,7 @@ # Gramps Web API - A RESTful API for the Gramps genealogy program # # Copyright (C) 2020 Christopher Horn +# Copyright (C) 2025 David Straub # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -25,7 +26,9 @@ import gramps.gen.filters as filters from flask import Response, abort from gramps.gen.db.base import DbReadBase +from gramps.gen.lib import Person from gramps.gen.filters import GenericFilter +from gramps.gen.filters.rules import Rule from marshmallow import Schema from webargs import ValidationError, fields, validate @@ -35,8 +38,27 @@ from . import ProtectedResource from .emit import GrampsJSONEncoder + +class HasAssociationType(Rule): + """Rule that checks for a person with an association of a given type.""" + + labels = ["Type:"] + name = "People with association of type " + description = "Matches people with a certain association type" + category = "General filters" + + def apply(self, db: DbReadBase, person: Person) -> bool: # type: ignore + for person_ref in person.get_person_ref_list(): + if person_ref.get_relation() == self.list[0]: + return True + return False + + +additional_person_rules = [HasAssociationType] + + _RULES_LOOKUP = { - "Person": filters.rules.person.editor_rule_list, + "Person": filters.rules.person.editor_rule_list + additional_person_rules, "Family": filters.rules.family.editor_rule_list, "Event": filters.rules.event.editor_rule_list, "Place": filters.rules.place.editor_rule_list, diff --git a/tests/test_endpoints/test_filters.py b/tests/test_endpoints/test_filters.py index b65affe8..7539eb4c 100644 --- a/tests/test_endpoints/test_filters.py +++ b/tests/test_endpoints/test_filters.py @@ -2,6 +2,7 @@ # Gramps Web API - A RESTful API for the Gramps genealogy program # # Copyright (C) 2020 Christopher Horn +# Copyright (C) 2025 David Straub # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -19,6 +20,7 @@ """Tests for the /api/filters endpoints using example_gramps.""" +import uuid import unittest from jsonschema import validate @@ -33,6 +35,7 @@ check_resource_missing, check_success, ) +from .util import fetch_header TEST_URL = BASE_URL + "/filters/" @@ -259,3 +262,54 @@ def setUpClass(cls): def test_filters_endpoint_notes_filter(self): """Test creation and application of a notes filter.""" check_filter_create_update_delete(self, BASE_URL, TEST_URL, "notes") + + +def make_handle() -> str: + """Make a new valid handle.""" + return str(uuid.uuid4()) + + +class TestHasAssociationType(unittest.TestCase): + """Test cases for the HasAssociationType filter.""" + + @classmethod + def setUpClass(cls): + """Test class setup.""" + cls.client = get_test_client() + + def test_has_association_type(self): + payload = {} + # check authorization required to post to endpoint + handle = make_handle() + payload = { + "_class": "Person", + "handle": handle, + "person_ref_list": [ + { + "_class": "PersonRef", + "rel": "DNA", + "ref": handle, # self-reference, why not + } + ], + } + headers = fetch_header(self.client) + url = '/api/people/?rules={"rules":[{"name":"HasAssociationType","values":["DNA"]}]}' + # no result + rv = self.client.get(url, headers=headers) + assert rv.json == [] + # add person + rv = self.client.post("/api/people/", json=payload, headers=headers) + assert rv.status_code == 201 + # one result + rv = self.client.get(url, headers=headers) + assert rv.json + assert rv.json[0]["handle"] == handle + # no result for wrong type + rv = self.client.get(url.replace("DNA", "NotDNA"), headers=headers) + assert rv.json == [] + # delete person + rv = self.client.delete(f"/api/people/{handle}", headers=headers) + assert rv.status_code == 200 + # no result + rv = self.client.get(url, headers=headers) + assert rv.json == []