Skip to content

Commit 3ad7aee

Browse files
authored
Implement sort and date filtering in memory (#433) (#434)
* Implement sort and date filtering in memory (#433) * Update license headers * Fix tests (the problem was order of elements with same sort value)
1 parent 07a05f2 commit 3ad7aee

File tree

4 files changed

+100
-106
lines changed

4 files changed

+100
-106
lines changed

gramps_webapi/api/resources/base.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#
22
# Gramps Web API - A RESTful API for the Gramps genealogy program
33
#
4-
# Copyright (C) 2020 David Straub
4+
# Copyright (C) 2020-2023 David Straub
55
#
66
# This program is free software; you can redistribute it and/or modify
77
# it under the terms of the GNU Affero General Public License as published by
@@ -21,9 +21,9 @@
2121

2222
import json
2323
from abc import abstractmethod
24-
from typing import Dict, List, Sequence
24+
from typing import Dict, List
2525

26-
from flask import Response, abort, current_app, request
26+
from flask import Response, abort, request
2727
from gramps.gen.const import GRAMPS_LOCALE as glocale
2828
from gramps.gen.db import DbTxn
2929
from gramps.gen.db.base import DbReadBase
@@ -33,8 +33,8 @@
3333
from gramps.gen.utils.grampslocale import GrampsLocale
3434
from webargs import fields, validate
3535

36-
from ...auth import get_tree_usage, set_tree_usage
3736
from ...auth.const import PERM_ADD_OBJ, PERM_DEL_OBJ, PERM_EDIT_OBJ
37+
from ...const import GRAMPS_OBJECT_PLURAL
3838
from ..auth import require_permissions
3939
from ..search import SearchIndexer
4040
from ..util import (
@@ -55,10 +55,10 @@
5555
from .util import (
5656
abort_with_message,
5757
add_object,
58+
filter_missing_files,
5859
fix_object_dict,
5960
get_backlinks,
6061
get_extended_attributes,
61-
get_missing_media_file_handles,
6262
get_reference_profile_for_object,
6363
get_soundex,
6464
hash_object,
@@ -109,18 +109,18 @@ def object_extend(
109109
return obj
110110

111111
def sort_objects(
112-
self, objs: List[str], args: Dict, locale: GrampsLocale = glocale
112+
self, objects: List[GrampsObject], args: Dict, locale: GrampsLocale = glocale
113113
) -> List:
114114
"""Sort the list of objects as needed."""
115115
return sort_objects(
116-
self.db_handle, self.gramps_class_name, objs, args, locale=locale
116+
self.db_handle, self.gramps_class_name, objects, args, locale=locale
117117
)
118118

119-
def match_dates(self, handles: List[str], date: str):
119+
def match_dates(self, objects: List[GrampsObject], date: str) -> List[GrampsObject]:
120120
"""If supported filter objects using date mask."""
121121
if self.gramps_class_name in ["Event", "Media", "Citation"]:
122-
return match_dates(self.db_handle, self.gramps_class_name, handles, date)
123-
return handles
122+
return match_dates(objects, date)
123+
return objects
124124

125125
@property
126126
def db_handle(self) -> DbReadBase:
@@ -387,41 +387,50 @@ def get(self, args: Dict) -> Response:
387387
200, [self.full_object(obj, args, locale=locale)], args, total_items=1
388388
)
389389

390+
# load all objects to memory
391+
objects_name = GRAMPS_OBJECT_PLURAL[self.gramps_class_name]
392+
iter_objects_method = self.db_handle.method("iter_%s", objects_name)
393+
objects = list(iter_objects_method())
394+
395+
# for all objects except events, repos, and notes, Gramps supports
396+
# a database-backed default sort order. Use that if no sort order
397+
# requested.
390398
query_method = self.db_handle.method("get_%s_handles", self.gramps_class_name)
391399
if self.gramps_class_name in ["Event", "Repository", "Note"]:
392400
handles = query_method()
393401
else:
394402
handles = query_method(sort_handles=True, locale=locale)
403+
handle_index = {handle: index for index, handle in enumerate(handles)}
404+
# sort objects by the sorted handle order
405+
objects = sorted(
406+
objects, key=lambda obj: handle_index.get(obj.handle, len(handles) + 1)
407+
)
395408

396409
if "filter" in args or "rules" in args:
410+
handles = [obj.handle for obj in objects]
397411
handles = apply_filter(
398412
self.db_handle, args, self.gramps_class_name, handles
399413
)
414+
objects = [obj for obj in objects if obj.handle in set(handles)]
400415

401416
if self.gramps_class_name == "Media" and args.get("filemissing"):
402-
handles = get_missing_media_file_handles(self.db_handle, handles)
417+
objects = filter_missing_files(objects)
403418

404419
if args["dates"]:
405-
handles = self.match_dates(handles, args["dates"])
420+
objects = self.match_dates(objects, args["dates"])
406421

407422
if "sort" in args:
408-
handles = self.sort_objects(handles, args["sort"], locale=locale)
423+
objects = self.sort_objects(objects, args["sort"], locale=locale)
409424

410-
total_items = len(handles)
425+
total_items = len(objects)
411426

412427
if args["page"] > 0:
413428
offset = (args["page"] - 1) * args["pagesize"]
414-
handles = handles[offset : offset + args["pagesize"]]
429+
objects = objects[offset : offset + args["pagesize"]]
415430

416-
query_method = self.db_handle.method(
417-
"get_%s_from_handle", self.gramps_class_name
418-
)
419431
return self.response(
420432
200,
421-
[
422-
self.full_object(query_method(handle), args, locale=locale)
423-
for handle in handles
424-
],
433+
[self.full_object(obj, args, locale=locale) for obj in objects],
425434
args,
426435
total_items=total_items,
427436
)
@@ -439,7 +448,7 @@ def post(self) -> Response:
439448
with DbTxn("Add objects", db_handle) as trans:
440449
try:
441450
add_object(db_handle, obj, trans, fail_if_exists=True)
442-
except ValueError as exc:
451+
except ValueError:
443452
abort_with_message(400, "Error while adding object")
444453
trans_dict = transaction_to_json(trans)
445454
# update usage

gramps_webapi/api/resources/match.py

Lines changed: 8 additions & 9 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) 2023 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
@@ -18,13 +19,12 @@
1819
#
1920

2021
"""Matching utilities."""
22+
2123
from typing import List
2224

23-
from gramps.gen.db.base import DbReadBase
2425
from gramps.gen.lib import Date
2526
from gramps.gen.lib.date import gregorian
26-
27-
from ...types import Handle
27+
from gramps.gen.lib.primaryobj import BasicPrimaryObject as GrampsObject
2828

2929

3030
def match_date(date: Date, mask: str) -> bool:
@@ -54,7 +54,8 @@ def match_date_range(date: Date, start_date: Date, end_date: Date) -> bool:
5454

5555

5656
def match_dates(
57-
db_handle: DbReadBase, gramps_class_name: str, handles: List[Handle], date_mask: str
57+
objects: List[GrampsObject],
58+
date_mask: str,
5859
):
5960
"""Match dates based on a date mask or range."""
6061
check_range = False
@@ -72,16 +73,14 @@ def match_dates(
7273
else:
7374
end_date = None
7475

75-
query_method = db_handle.method("get_%s_from_handle", gramps_class_name)
7676
result = []
77-
for handle in handles:
78-
obj = query_method(handle)
77+
for obj in objects:
7978
date = obj.get_date_object()
8079
if date.is_valid():
8180
if check_range:
8281
if match_date_range(date, start_date, end_date):
83-
result.append(handle)
82+
result.append(obj)
8483
else:
8584
if match_date(date, date_mask):
86-
result.append(handle)
85+
result.append(obj)
8786
return result

0 commit comments

Comments
 (0)