Skip to content

Commit 1833899

Browse files
authored
Add association type filter rule (#602)
1 parent e52da84 commit 1833899

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

gramps_webapi/api/resources/filters.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Gramps Web API - A RESTful API for the Gramps genealogy program
33
#
44
# Copyright (C) 2020 Christopher Horn
5+
# Copyright (C) 2025 David Straub
56
#
67
# This program is free software; you can redistribute it and/or modify
78
# it under the terms of the GNU Affero General Public License as published by
@@ -25,7 +26,9 @@
2526
import gramps.gen.filters as filters
2627
from flask import Response, abort
2728
from gramps.gen.db.base import DbReadBase
29+
from gramps.gen.lib import Person
2830
from gramps.gen.filters import GenericFilter
31+
from gramps.gen.filters.rules import Rule
2932
from marshmallow import Schema
3033
from webargs import ValidationError, fields, validate
3134

@@ -35,8 +38,27 @@
3538
from . import ProtectedResource
3639
from .emit import GrampsJSONEncoder
3740

41+
42+
class HasAssociationType(Rule):
43+
"""Rule that checks for a person with an association of a given type."""
44+
45+
labels = ["Type:"]
46+
name = "People with association of type <type>"
47+
description = "Matches people with a certain association type"
48+
category = "General filters"
49+
50+
def apply(self, db: DbReadBase, person: Person) -> bool: # type: ignore
51+
for person_ref in person.get_person_ref_list():
52+
if person_ref.get_relation() == self.list[0]:
53+
return True
54+
return False
55+
56+
57+
additional_person_rules = [HasAssociationType]
58+
59+
3860
_RULES_LOOKUP = {
39-
"Person": filters.rules.person.editor_rule_list,
61+
"Person": filters.rules.person.editor_rule_list + additional_person_rules,
4062
"Family": filters.rules.family.editor_rule_list,
4163
"Event": filters.rules.event.editor_rule_list,
4264
"Place": filters.rules.place.editor_rule_list,

tests/test_endpoints/test_filters.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Gramps Web API - A RESTful API for the Gramps genealogy program
33
#
44
# Copyright (C) 2020 Christopher Horn
5+
# Copyright (C) 2025 David Straub
56
#
67
# This program is free software; you can redistribute it and/or modify
78
# it under the terms of the GNU Affero General Public License as published by
@@ -19,6 +20,7 @@
1920

2021
"""Tests for the /api/filters endpoints using example_gramps."""
2122

23+
import uuid
2224
import unittest
2325

2426
from jsonschema import validate
@@ -33,6 +35,7 @@
3335
check_resource_missing,
3436
check_success,
3537
)
38+
from .util import fetch_header
3639

3740
TEST_URL = BASE_URL + "/filters/"
3841

@@ -259,3 +262,54 @@ def setUpClass(cls):
259262
def test_filters_endpoint_notes_filter(self):
260263
"""Test creation and application of a notes filter."""
261264
check_filter_create_update_delete(self, BASE_URL, TEST_URL, "notes")
265+
266+
267+
def make_handle() -> str:
268+
"""Make a new valid handle."""
269+
return str(uuid.uuid4())
270+
271+
272+
class TestHasAssociationType(unittest.TestCase):
273+
"""Test cases for the HasAssociationType filter."""
274+
275+
@classmethod
276+
def setUpClass(cls):
277+
"""Test class setup."""
278+
cls.client = get_test_client()
279+
280+
def test_has_association_type(self):
281+
payload = {}
282+
# check authorization required to post to endpoint
283+
handle = make_handle()
284+
payload = {
285+
"_class": "Person",
286+
"handle": handle,
287+
"person_ref_list": [
288+
{
289+
"_class": "PersonRef",
290+
"rel": "DNA",
291+
"ref": handle, # self-reference, why not
292+
}
293+
],
294+
}
295+
headers = fetch_header(self.client)
296+
url = '/api/people/?rules={"rules":[{"name":"HasAssociationType","values":["DNA"]}]}'
297+
# no result
298+
rv = self.client.get(url, headers=headers)
299+
assert rv.json == []
300+
# add person
301+
rv = self.client.post("/api/people/", json=payload, headers=headers)
302+
assert rv.status_code == 201
303+
# one result
304+
rv = self.client.get(url, headers=headers)
305+
assert rv.json
306+
assert rv.json[0]["handle"] == handle
307+
# no result for wrong type
308+
rv = self.client.get(url.replace("DNA", "NotDNA"), headers=headers)
309+
assert rv.json == []
310+
# delete person
311+
rv = self.client.delete(f"/api/people/{handle}", headers=headers)
312+
assert rv.status_code == 200
313+
# no result
314+
rv = self.client.get(url, headers=headers)
315+
assert rv.json == []

0 commit comments

Comments
 (0)