11"""Gramps filter interface."""
22
33import json
4- from typing import Dict , List , Set
4+ from typing import Any , Dict , List , Set
55
66import gramps .gen .filters as filters
77from flask import Response , abort
88from gramps .gen .db .base import DbReadBase
99from gramps .gen .filters import GenericFilter
10- from marshmallow import Schema , ValidationError , fields
11- from webargs import validate
10+ from marshmallow import Schema
11+ from webargs import ValidationError , fields , validate
1212from webargs .flaskparser import use_args
1313
1414from ...const import GRAMPS_NAMESPACES
2929}
3030
3131
32- def get_filter_rules (args : Dict [str , str ], namespace : str ) -> List [Dict ]:
32+ def get_filter_rules (args : Dict [str , Any ], namespace : str ) -> List [Dict ]:
3333 """Return a list of available filter rules for a namespace."""
3434 rule_list = []
3535 for rule_class in _RULES_LOOKUP [namespace ]:
3636 add_rule = True
37- if args .get ("rule" ) and args ["rule" ] != rule_class .__name__ :
38- add_rule = False
37+ if "rules" in args and args ["rules" ]:
38+ if rule_class .__name__ not in args ["rules" ]:
39+ add_rule = False
3940 if add_rule :
4041 rule_list .append (
4142 {
@@ -46,17 +47,20 @@ def get_filter_rules(args: Dict[str, str], namespace: str) -> List[Dict]:
4647 "rule" : rule_class .__name__ ,
4748 }
4849 )
50+ if "rules" in args and len (args ["rules" ]) != len (rule_list ):
51+ abort (404 )
4952 return rule_list
5053
5154
52- def get_custom_filters (args : Dict [str , str ], namespace : str ) -> List [Dict ]:
55+ def get_custom_filters (args : Dict [str , Any ], namespace : str ) -> List [Dict ]:
5356 """Return a list of custom filters for a namespace."""
5457 filter_list = []
5558 filters .reload_custom_filters ()
5659 for filter_class in filters .CustomFilters .get_filters (namespace ):
5760 add_filter = True
58- if args .get ("filter" ) and args ["filter" ] != filter_class .get_name ():
59- add_filter = False
61+ if "filters" in args and args ["filters" ]:
62+ if filter_class .get_name () not in args ["filters" ]:
63+ add_filter = False
6064 if add_filter :
6165 filter_list .append (
6266 {
@@ -74,6 +78,8 @@ def get_custom_filters(args: Dict[str, str], namespace: str) -> List[Dict]:
7478 ],
7579 }
7680 )
81+ if "filters" in args and len (args ["filters" ]) != len (filter_list ):
82+ abort (404 )
7783 return filter_list
7884
7985
@@ -113,7 +119,7 @@ def apply_filter(db_handle: DbReadBase, args: Dict, namespace: str) -> List[Hand
113119 for filter_class in filters .CustomFilters .get_filters (namespace ):
114120 if args ["filter" ] == filter_class .get_name ():
115121 return filter_class .apply (db_handle )
116- abort (400 )
122+ abort (404 )
117123
118124 try :
119125 filter_parms = FilterSchema ().load (json .loads (args ["rules" ]))
@@ -143,21 +149,31 @@ class FilterSchema(Schema):
143149 validate = validate .OneOf (["and" , "or" , "xor" , "one" ]),
144150 )
145151 invert = fields .Boolean (required = False , missing = False )
146- rules = fields .List (fields .Nested (RuleSchema ))
152+ rules = fields .List (
153+ fields .Nested (RuleSchema ), required = True , validate = validate .Length (min = 1 )
154+ )
147155
148156
149157class CustomFilterSchema (FilterSchema ):
150158 """Structure for a custom filter."""
151159
152160 name = fields .Str (required = True , validate = validate .Length (min = 1 ))
153161 comment = fields .Str (required = False )
162+ rules = fields .List (
163+ fields .Nested (RuleSchema ), required = True , validate = validate .Length (min = 1 )
164+ )
154165
155166
156- class FilterResource (ProtectedResource , GrampsJSONEncoder ):
157- """Filter resource."""
167+ class FiltersResource (ProtectedResource , GrampsJSONEncoder ):
168+ """Filters resource."""
158169
159170 @use_args (
160- {"filter" : fields .Str (), "rule" : fields .Str ()},
171+ {
172+ "filters" : fields .DelimitedList (
173+ fields .Str (validate = validate .Length (min = 1 ))
174+ ),
175+ "rules" : fields .DelimitedList (fields .Str (validate = validate .Length (min = 1 ))),
176+ },
161177 location = "query" ,
162178 )
163179 def get (self , args : Dict [str , str ], namespace : str ) -> Response :
@@ -168,10 +184,10 @@ def get(self, args: Dict[str, str], namespace: str) -> Response:
168184 abort (404 )
169185
170186 rule_list = get_filter_rules (args , namespace )
171- if args . get ( "rule" ) and not args . get ( "filter" ) :
187+ if "rules" in args and "filters" not in args :
172188 return self .response (200 , {"rules" : rule_list })
173189 filter_list = get_custom_filters (args , namespace )
174- if args . get ( "filter" ) and not args . get ( "rule" ) :
190+ if "filters" in args and "rules" not in args :
175191 return self .response (200 , {"filters" : filter_list })
176192 return self .response (200 , {"filters" : filter_list , "rules" : rule_list })
177193
@@ -212,14 +228,30 @@ def put(self, args: Dict, namespace: str) -> Response:
212228 )
213229 return abort (404 )
214230
231+
232+ class FilterResource (ProtectedResource , GrampsJSONEncoder ):
233+ """Filter resource."""
234+
235+ def get (self , namespace : str , name : str ) -> Response :
236+ """Get a custom filter."""
237+ try :
238+ namespace = GRAMPS_NAMESPACES [namespace ]
239+ except KeyError :
240+ abort (404 )
241+
242+ args = {"filters" : [name ]}
243+ filter_list = get_custom_filters (args , namespace )
244+ if len (filter_list ) == 0 :
245+ abort (404 )
246+ return self .response (200 , filter_list [0 ])
247+
215248 @use_args (
216249 {
217- "name" : fields .Str (required = True ),
218- "force" : fields .Boolean (required = False , missing = False ),
250+ "force" : fields .Str (validate = validate .Length (equal = 0 )),
219251 },
220- location = "json " ,
252+ location = "query " ,
221253 )
222- def delete (self , args : Dict , namespace : str ) -> Response :
254+ def delete (self , args : Dict , namespace : str , name : str ) -> Response :
223255 """Delete a custom filter."""
224256 try :
225257 namespace = GRAMPS_NAMESPACES [namespace ]
@@ -229,17 +261,15 @@ def delete(self, args: Dict, namespace: str) -> Response:
229261 filters .reload_custom_filters ()
230262 custom_filters = filters .CustomFilters .get_filters (namespace )
231263 for custom_filter in custom_filters :
232- if args [ " name" ] == custom_filter .get_name ():
264+ if name == custom_filter .get_name ():
233265 filter_set = set ()
234266 self ._find_dependent_filters (namespace , custom_filter , filter_set )
235267 if len (filter_set ) > 1 :
236268 if "force" not in args or not args ["force" ]:
237269 abort (405 )
238270 list (map (custom_filters .remove , filter_set ))
239271 filters .CustomFilters .save ()
240- return self .response (
241- 200 , {"message" : "Deleted filter: " + args ["name" ]}
242- )
272+ return self .response (200 , {"message" : "Deleted filter: " + name })
243273 return abort (404 )
244274
245275 def _find_dependent_filters (
0 commit comments