Skip to content

Commit 3051821

Browse files
authored
Add filtering support, rename endpoints, refactor object extend (closes #17) (#30)
* Add filtering support, rename endpoints, refactor object extend * Remove type hint that breaks Travis Python3.5 test * Fix overlooked edit from switching filters= to rules= * Fix default dict, fix indent bug * Refactor rules query parm, remove handle query parm
1 parent 75e0355 commit 3051821

File tree

22 files changed

+1267
-392
lines changed

22 files changed

+1267
-392
lines changed

gramps_webapi/api/__init__.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .resources.citation import CitationResource, CitationsResource
1111
from .resources.event import EventResource, EventsResource
1212
from .resources.family import FamiliesResource, FamilyResource
13+
from .resources.filters import FilterResource
1314
from .resources.media import MediaObjectResource, MediaObjectsResource
1415
from .resources.metadata import MetadataResource
1516
from .resources.note import NoteResource, NotesResource
@@ -31,49 +32,51 @@ def register_endpt(resource: Type[Resource], url: str, name: str):
3132

3233

3334
# Person
34-
register_endpt(PersonResource, "/person/<string:handle>", "person")
35-
register_endpt(PeopleResource, "/person/", "people")
35+
register_endpt(PersonResource, "/people/<string:handle>", "person")
36+
register_endpt(PeopleResource, "/people/", "people")
3637
# Family
37-
register_endpt(FamilyResource, "/family/<string:handle>", "family")
38-
register_endpt(FamiliesResource, "/family/", "families")
38+
register_endpt(FamilyResource, "/families/<string:handle>", "family")
39+
register_endpt(FamiliesResource, "/families/", "families")
3940
# Source
40-
register_endpt(SourceResource, "/source/<string:handle>", "source")
41-
register_endpt(SourcesResource, "/source/", "sources")
41+
register_endpt(SourceResource, "/sources/<string:handle>", "source")
42+
register_endpt(SourcesResource, "/sources/", "sources")
4243
# Citation
43-
register_endpt(CitationResource, "/citation/<string:handle>", "citation")
44-
register_endpt(CitationsResource, "/citation/", "citations")
44+
register_endpt(CitationResource, "/citations/<string:handle>", "citation")
45+
register_endpt(CitationsResource, "/citations/", "citations")
4546
# Event
46-
register_endpt(EventResource, "/event/<string:handle>", "event")
47-
register_endpt(EventsResource, "/event/", "events")
47+
register_endpt(EventResource, "/events/<string:handle>", "event")
48+
register_endpt(EventsResource, "/events/", "events")
4849
# Media Object
4950
register_endpt(MediaObjectResource, "/media/<string:handle>", "media_object")
5051
register_endpt(MediaObjectsResource, "/media/", "media_objects")
5152
# Place
52-
register_endpt(PlaceResource, "/place/<string:handle>", "place")
53-
register_endpt(PlacesResource, "/place/", "places")
53+
register_endpt(PlaceResource, "/places/<string:handle>", "place")
54+
register_endpt(PlacesResource, "/places/", "places")
5455
# Repository
55-
register_endpt(RepositoryResource, "/repository/<string:handle>", "repository")
56-
register_endpt(RepositoriesResource, "/repository/", "repositories")
56+
register_endpt(RepositoryResource, "/repositories/<string:handle>", "repository")
57+
register_endpt(RepositoriesResource, "/repositories/", "repositories")
5758
# Note
58-
register_endpt(NoteResource, "/note/<string:handle>", "note")
59-
register_endpt(NotesResource, "/note/", "notes")
59+
register_endpt(NoteResource, "/notes/<string:handle>", "note")
60+
register_endpt(NotesResource, "/notes/", "notes")
6061
# Tag
61-
register_endpt(TagResource, "/tag/<string:handle>", "tag")
62-
register_endpt(TagsResource, "/tag/", "tags")
62+
register_endpt(TagResource, "/tags/<string:handle>", "tag")
63+
register_endpt(TagsResource, "/tags/", "tags")
6364
# Token
6465
register_endpt(TokenResource, "/login/", "token")
6566
register_endpt(TokenRefreshResource, "/refresh/", "token_refresh")
6667
# Bookmark
67-
register_endpt(BookmarkResource, "/bookmark/<string:category>", "bookmark")
68-
register_endpt(BookmarksResource, "/bookmark/", "bookmarks")
68+
register_endpt(BookmarkResource, "/bookmarks/<string:namespace>", "bookmark")
69+
register_endpt(BookmarksResource, "/bookmarks/", "bookmarks")
70+
# Filter
71+
register_endpt(FilterResource, "/filters/<string:namespace>", "filter")
6972
# Translate
70-
register_endpt(TranslationResource, "/translate/<string:code>", "translation")
71-
register_endpt(TranslationsResource, "/translate/", "translations")
73+
register_endpt(TranslationResource, "/translations/<string:code>", "translation")
74+
register_endpt(TranslationsResource, "/translations/", "translations")
7275
# Relation
7376
register_endpt(
7477
RelationResource,
75-
"/relation/<string:handle1>/<string:handle2>",
76-
"relation",
78+
"/relations/<string:handle1>/<string:handle2>",
79+
"relations",
7780
)
7881
# Metadata
7982
register_endpt(MetadataResource, "/metadata/<string:datatype>", "metadata")

gramps_webapi/api/resources/base.py

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
from gramps.gen.db.base import DbReadBase
88
from gramps.gen.errors import HandleError
99
from gramps.gen.lib.primaryobj import BasicPrimaryObject as GrampsObject
10-
from webargs import fields
10+
from webargs import fields, validate
1111
from webargs.flaskparser import use_args
1212

1313
from ..util import get_dbstate
1414
from . import ProtectedResource, Resource
1515
from .emit import GrampsJSONEncoder
16+
from .filters import apply_filter
17+
from .util import get_extended_attributes
1618

1719

1820
class GrampsObjectResourceHelper(GrampsJSONEncoder):
@@ -23,9 +25,11 @@ class GrampsObjectResourceHelper(GrampsJSONEncoder):
2325
def gramps_class_name(self):
2426
"""To be set on child classes."""
2527

26-
@abstractmethod
2728
def object_extend(self, obj: GrampsObject, args: Dict) -> GrampsObject:
2829
"""Extend the base object attributes as needed."""
30+
if "extend" in args:
31+
obj.extended = get_extended_attributes(self.db_handle, obj)
32+
return obj
2933

3034
@property
3135
def db_handle(self) -> DbReadBase:
@@ -52,11 +56,11 @@ class GrampsObjectResource(GrampsObjectResourceHelper, Resource):
5256

5357
@use_args(
5458
{
55-
"strip": fields.Boolean(missing=False),
56-
"keys": fields.DelimitedList(fields.Str()),
59+
"strip": fields.Str(validate=validate.Length(equal=0)),
60+
"keys": fields.DelimitedList(fields.Str(), missing=[]),
5761
"skipkeys": fields.DelimitedList(fields.Str(), missing=[]),
58-
"profile": fields.Boolean(missing=False),
59-
"extend": fields.Boolean(missing=False),
62+
"profile": fields.Str(validate=validate.Length(equal=0)),
63+
"extend": fields.Str(validate=validate.Length(equal=0)),
6064
},
6165
location="query",
6266
)
@@ -65,22 +69,24 @@ def get(self, args: Dict, handle: str) -> Response:
6569
try:
6670
obj = self.get_object_from_handle(handle)
6771
except HandleError:
68-
return abort(404)
69-
return self.response(self.object_extend(obj, args), args)
72+
abort(404)
73+
return self.response(200, self.object_extend(obj, args), args)
7074

7175

7276
class GrampsObjectsResource(GrampsObjectResourceHelper, Resource):
7377
"""Resource for multiple objects."""
7478

7579
@use_args(
7680
{
77-
"gramps_id": fields.Str(),
78-
"handle": fields.Str(),
79-
"strip": fields.Boolean(missing=False),
80-
"keys": fields.DelimitedList(fields.Str()),
81+
"gramps_id": fields.Str(validate=validate.Length(min=1)),
82+
"handle": fields.Str(validate=validate.Length(min=1)),
83+
"strip": fields.Str(validate=validate.Length(equal=0)),
84+
"keys": fields.DelimitedList(fields.Str(), missing=[]),
8185
"skipkeys": fields.DelimitedList(fields.Str(), missing=[]),
82-
"profile": fields.Boolean(missing=False),
83-
"extend": fields.Boolean(missing=False),
86+
"profile": fields.Str(validate=validate.Length(equal=0)),
87+
"extend": fields.Str(validate=validate.Length(equal=0)),
88+
"filter": fields.Str(validate=validate.Length(min=1)),
89+
"rules": fields.Str(validate=validate.Length(min=1)),
8490
},
8591
location="query",
8692
)
@@ -89,19 +95,24 @@ def get(self, args: Dict) -> Response:
8995
if "gramps_id" in args:
9096
obj = self.get_object_from_gramps_id(args["gramps_id"])
9197
if obj is None:
92-
return abort(404)
93-
return self.response([self.object_extend(obj, args)], args)
94-
if "handle" in args:
95-
try:
96-
obj = self.get_object_from_handle(args["handle"])
97-
except HandleError:
98-
return abort(404)
99-
return self.response([self.object_extend(obj, args)], args)
100-
iter_method = self.db_handle.method("iter_%s_handles", self.gramps_class_name)
98+
abort(404)
99+
return self.response(200, [self.object_extend(obj, args)], args)
101100
query_method = self.db_handle.method(
102101
"get_%s_from_handle", self.gramps_class_name
103102
)
103+
if "filter" in args or "rules" in args:
104+
handle_list = apply_filter(self.db_handle, args, self.gramps_class_name)
105+
return self.response(
106+
200,
107+
[
108+
self.object_extend(query_method(handle), args)
109+
for handle in handle_list
110+
],
111+
args,
112+
)
113+
iter_method = self.db_handle.method("iter_%s_handles", self.gramps_class_name)
104114
return self.response(
115+
200,
105116
[
106117
self.object_extend(query_method(handle), args)
107118
for handle in iter_method()

gramps_webapi/api/resources/bookmark.py

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,30 @@ def db_handle(self) -> DbReadBase:
1616
"""Get the database instance."""
1717
return get_dbstate().db
1818

19-
def get(self, category: str) -> Response:
20-
"""Get list of bookmarks by category."""
19+
def get(self, namespace: str) -> Response:
20+
"""Get list of bookmarks by namespace."""
2121
db_handle = self.db_handle
22-
if category == "person":
22+
if namespace == "people":
2323
result = db_handle.get_bookmarks()
24-
elif category == "family":
24+
elif namespace == "families":
2525
result = db_handle.get_family_bookmarks()
26-
elif category == "media":
26+
elif namespace == "media":
2727
result = db_handle.get_media_bookmarks()
28-
elif category == "event":
28+
elif namespace == "events":
2929
result = db_handle.get_event_bookmarks()
30-
elif category == "citation":
30+
elif namespace == "citations":
3131
result = db_handle.get_citation_bookmarks()
32-
elif category == "note":
32+
elif namespace == "notes":
3333
result = db_handle.get_note_bookmarks()
34-
elif category == "place":
34+
elif namespace == "places":
3535
result = db_handle.get_place_bookmarks()
36-
elif category == "source":
36+
elif namespace == "sources":
3737
result = db_handle.get_source_bookmarks()
38-
elif category == "repository":
38+
elif namespace == "repositories":
3939
result = db_handle.get_repo_bookmarks()
4040
else:
41-
return abort(404)
42-
return self.response(result)
41+
abort(404)
42+
return self.response(200, result)
4343

4444

4545
class BookmarksResource(ProtectedResource, GrampsJSONEncoder):
@@ -48,17 +48,18 @@ class BookmarksResource(ProtectedResource, GrampsJSONEncoder):
4848
def get(self) -> Response:
4949
"""Get the list of bookmark types."""
5050
return self.response(
51+
200,
5152
{
52-
"categories": [
53-
"citation",
54-
"event",
55-
"family",
53+
"namespaces": [
54+
"citations",
55+
"events",
56+
"families",
5657
"media",
57-
"note",
58-
"person",
59-
"place",
60-
"repository",
61-
"source",
58+
"notes",
59+
"people",
60+
"places",
61+
"repositories",
62+
"sources",
6263
]
63-
}
64+
},
6465
)

gramps_webapi/api/resources/citation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class CitationResourceHelper(GrampsObjectResourceHelper):
1919

2020
def object_extend(self, obj: Citation, args: Dict) -> Citation:
2121
"""Extend citation attributes as needed."""
22-
if args["extend"]:
22+
if "extend" in args:
2323
db_handle = self.db_handle
2424
obj.extended = get_extended_attributes(db_handle, obj)
2525
obj.extended["source"] = get_source_by_handle(db_handle, obj.source_handle)

gramps_webapi/api/resources/emit.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Gramps Json Encoder."""
22

33
import inspect
4-
from typing import Any, Dict
4+
from typing import Any, Dict, Optional
55

66
import gramps.gen.lib as lib
77
from flask import Response
@@ -26,19 +26,23 @@ def __init__(self):
2626
getattr(lib, key) for key, value in inspect.getmembers(lib, inspect.isclass)
2727
]
2828

29-
def response(self, payload: Any, args: Dict = {}) -> Response:
30-
# pylint:disable=dangerous-default-value
29+
def response(
30+
self, status: int = 200, payload: Any = {}, args: Optional[Dict] = None
31+
) -> Response:
3132
"""Prepare response."""
33+
args = args or {}
3234
if "strip" in args:
33-
self.strip_empty_keys = args["strip"]
35+
self.strip_empty_keys = True
36+
else:
37+
self.strip_empty_keys = False
3438
if "keys" in args:
3539
self.filter_only_keys = args["keys"]
3640
if "skipkeys" in args:
3741
self.filter_skip_keys = args["skipkeys"]
3842

3943
return Response(
4044
response=self.encode(payload),
41-
status=200,
45+
status=status,
4246
mimetype="application/json",
4347
)
4448

gramps_webapi/api/resources/event.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ class EventResourceHelper(GrampsObjectResourceHelper):
2424
def object_extend(self, obj: Event, args: Dict) -> Event:
2525
"""Extend event attributes as needed."""
2626
db_handle = self.db_handle
27-
if args["profile"]:
27+
if "profile" in args:
2828
obj.profile = get_event_profile_for_object(db_handle, obj)
29-
if args["extend"]:
29+
if "extend" in args:
3030
obj.extended = get_extended_attributes(db_handle, obj)
3131
obj.extended["place"] = get_place_by_handle(db_handle, obj.place)
3232
return obj

gramps_webapi/api/resources/family.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ class FamilyResourceHelper(GrampsObjectResourceHelper):
2424
def object_extend(self, obj: Family, args: Dict) -> Family:
2525
"""Extend family attributes as needed."""
2626
db_handle = self.db_handle
27-
if args["profile"]:
27+
if "profile" in args:
2828
obj.profile = get_family_profile_for_object(
2929
db_handle, obj, with_events=True
3030
)
31-
if args["extend"]:
31+
if "extend" in args:
3232
obj.extended = get_extended_attributes(db_handle, obj)
3333
obj.extended["father"] = get_person_by_handle(db_handle, obj.father_handle)
3434
obj.extended["mother"] = get_person_by_handle(db_handle, obj.mother_handle)

0 commit comments

Comments
 (0)