Remove Django-style WHERE; consider Python expressions
This commit is contained in:
parent
3f7b441a54
commit
f093c8bd79
@ -35,6 +35,7 @@ install:
|
|||||||
# - cd $TRAVIS_BUILD_DIR
|
# - cd $TRAVIS_BUILD_DIR
|
||||||
# $TRAVIS_BUILD_DIR is set to the location of the cloned repository:
|
# $TRAVIS_BUILD_DIR is set to the location of the cloned repository:
|
||||||
# for example: /home/travis/build/gramps-project/gramps
|
# for example: /home/travis/build/gramps-project/gramps
|
||||||
|
- git clone -b master https://github.com/srossross/meta
|
||||||
- python setup.py build
|
- python setup.py build
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
@ -45,7 +46,7 @@ before_script:
|
|||||||
script:
|
script:
|
||||||
# --exclude=TestUser because of older version of mock
|
# --exclude=TestUser because of older version of mock
|
||||||
# without configure_mock
|
# without configure_mock
|
||||||
- GRAMPS_RESOURCES=. nosetests3 --nologcapture --with-coverage --cover-package=gramps --exclude=TestcaseGenerator --exclude=vcard --exclude=merge_ref_test --exclude=user_test gramps
|
- PYTHONPATH=meta GRAMPS_RESOURCES=. nosetests3 --nologcapture --with-coverage --cover-package=gramps --exclude=TestcaseGenerator --exclude=vcard --exclude=merge_ref_test --exclude=user_test gramps
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- codecov
|
- codecov
|
||||||
|
@ -1287,7 +1287,7 @@ class DbReadBase(object):
|
|||||||
if compare(item, op, value):
|
if compare(item, op, value):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
if op == "=":
|
if op in ["=", "=="]:
|
||||||
matched = v == value
|
matched = v == value
|
||||||
elif op == ">":
|
elif op == ">":
|
||||||
matched = v > value
|
matched = v > value
|
||||||
@ -1430,15 +1430,15 @@ class DbReadBase(object):
|
|||||||
name = self.get_table_func(table,"class_func").get_field_alias(name)
|
name = self.get_table_func(table,"class_func").get_field_alias(name)
|
||||||
return name.replace(".", "__")
|
return name.replace(".", "__")
|
||||||
|
|
||||||
Person = property(lambda self:QuerySet(self, "Person"))
|
Person = property(lambda self: QuerySet(self, "Person"))
|
||||||
Family = property(lambda self:QuerySet(self, "Family"))
|
Family = property(lambda self: QuerySet(self, "Family"))
|
||||||
Note = property(lambda self:QuerySet(self, "Note"))
|
Note = property(lambda self: QuerySet(self, "Note"))
|
||||||
Citation = property(lambda self:QuerySet(self, "Citation"))
|
Citation = property(lambda self: QuerySet(self, "Citation"))
|
||||||
Source = property(lambda self:QuerySet(self, "Source"))
|
Source = property(lambda self: QuerySet(self, "Source"))
|
||||||
Repository = property(lambda self:QuerySet(self, "Repository"))
|
Repository = property(lambda self: QuerySet(self, "Repository"))
|
||||||
Place = property(lambda self:QuerySet(self, "Place"))
|
Place = property(lambda self: QuerySet(self, "Place"))
|
||||||
Event = property(lambda self:QuerySet(self, "Event"))
|
Event = property(lambda self: QuerySet(self, "Event"))
|
||||||
Tag = property(lambda self:QuerySet(self, "Tag"))
|
Tag = property(lambda self: QuerySet(self, "Tag"))
|
||||||
|
|
||||||
class DbWriteBase(DbReadBase):
|
class DbWriteBase(DbReadBase):
|
||||||
"""
|
"""
|
||||||
@ -2089,41 +2089,6 @@ class DbWriteBase(DbReadBase):
|
|||||||
"""
|
"""
|
||||||
return getattr(self, table_name)
|
return getattr(self, table_name)
|
||||||
|
|
||||||
class Operator(object):
|
|
||||||
"""
|
|
||||||
Base for QuerySet operators.
|
|
||||||
"""
|
|
||||||
op = "OP"
|
|
||||||
def __init__(self, *expressions, **kwargs):
|
|
||||||
if self.op in ["AND", "OR"]:
|
|
||||||
exprs = [expression.list for expression
|
|
||||||
in expressions]
|
|
||||||
for key in kwargs:
|
|
||||||
exprs.append(
|
|
||||||
_select_field_operator_value(key, "=", kwargs[key]))
|
|
||||||
else: # "NOT"
|
|
||||||
if expressions:
|
|
||||||
exprs = expressions.list
|
|
||||||
else:
|
|
||||||
key, value = list(kwargs.items())[0]
|
|
||||||
exprs = _select_field_operator_value(key, "=", value)
|
|
||||||
self.list = [self.op, exprs]
|
|
||||||
|
|
||||||
class AND(Operator):
|
|
||||||
op = "AND"
|
|
||||||
|
|
||||||
class OR(Operator):
|
|
||||||
"""
|
|
||||||
OR operator for QuerySet logical WHERE expressions.
|
|
||||||
"""
|
|
||||||
op = "OR"
|
|
||||||
|
|
||||||
class NOT(Operator):
|
|
||||||
"""
|
|
||||||
NOT operator for QuerySet logical WHERE expressions.
|
|
||||||
"""
|
|
||||||
op = "NOT"
|
|
||||||
|
|
||||||
class QuerySet(object):
|
class QuerySet(object):
|
||||||
"""
|
"""
|
||||||
A container for selection criteria before being actually
|
A container for selection criteria before being actually
|
||||||
@ -2164,20 +2129,15 @@ class QuerySet(object):
|
|||||||
self.needs_to_run = True
|
self.needs_to_run = True
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _add_where_clause(self, *args, **kwargs):
|
def _add_where_clause(self, *args):
|
||||||
"""
|
"""
|
||||||
Add a condition to the where clause.
|
Add a condition to the where clause.
|
||||||
"""
|
"""
|
||||||
# First, handle AND, OR, NOT args:
|
# First, handle AND, OR, NOT args:
|
||||||
and_expr = []
|
and_expr = []
|
||||||
for arg in args:
|
for expr in args:
|
||||||
expr = arg.list
|
|
||||||
and_expr.append(expr)
|
and_expr.append(expr)
|
||||||
# Next, handle kwargs:
|
# Next, handle kwargs:
|
||||||
for keyword in kwargs:
|
|
||||||
and_expr.append(
|
|
||||||
_select_field_operator_value(
|
|
||||||
keyword, "=", kwargs[keyword]))
|
|
||||||
if and_expr:
|
if and_expr:
|
||||||
if self.where_by:
|
if self.where_by:
|
||||||
self.where_by = ["AND", [self.where_by] + and_expr]
|
self.where_by = ["AND", [self.where_by] + and_expr]
|
||||||
@ -2260,20 +2220,32 @@ class QuerySet(object):
|
|||||||
self.database = proxy_class(self.database, *args, **kwargs)
|
self.database = proxy_class(self.database, *args, **kwargs)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def filter(self, *args, **kwargs):
|
def where(self, where_clause):
|
||||||
|
"""
|
||||||
|
Apply a where_clause (closure) to the selection process.
|
||||||
|
"""
|
||||||
|
from gramps.gen.db.where import eval_where
|
||||||
|
# if there is already a generator, then error:
|
||||||
|
if self.generator:
|
||||||
|
raise Exception("Queries in invalid order")
|
||||||
|
where_by = eval_where(where_clause)
|
||||||
|
self._add_where_clause(where_by)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def filter(self, *args):
|
||||||
"""
|
"""
|
||||||
Apply a filter to the database.
|
Apply a filter to the database.
|
||||||
"""
|
"""
|
||||||
from gramps.gen.proxy import FilterProxyDb
|
from gramps.gen.proxy import FilterProxyDb
|
||||||
from gramps.gen.filters import GenericFilter
|
from gramps.gen.filters import GenericFilter
|
||||||
|
from gramps.gen.db.where import eval_where
|
||||||
for i in range(len(args)):
|
for i in range(len(args)):
|
||||||
arg = args[i]
|
arg = args[i]
|
||||||
if isinstance(arg, GenericFilter):
|
if isinstance(arg, GenericFilter):
|
||||||
self.database = FilterProxyDb(self.database, arg, *args[i+1:])
|
self.database = FilterProxyDb(self.database, arg, *args[i+1:])
|
||||||
if arg.where_by:
|
if hasattr(arg, "where"):
|
||||||
self._add_where_clause(arg.where_by)
|
where_by = eval_where(arg.where)
|
||||||
elif isinstance(arg, Operator):
|
self._add_where_clause(where_by)
|
||||||
self._add_where_clause(arg)
|
|
||||||
elif callable(arg):
|
elif callable(arg):
|
||||||
if self.generator and self.needs_to_run:
|
if self.generator and self.needs_to_run:
|
||||||
## error
|
## error
|
||||||
@ -2285,8 +2257,6 @@ class QuerySet(object):
|
|||||||
self.generator = filter(arg, self.generator)
|
self.generator = filter(arg, self.generator)
|
||||||
else:
|
else:
|
||||||
pass # ignore, may have been arg from previous Filter
|
pass # ignore, may have been arg from previous Filter
|
||||||
if kwargs:
|
|
||||||
self._add_where_clause(**kwargs)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def map(self, f):
|
def map(self, f):
|
||||||
@ -2329,33 +2299,3 @@ class QuerySet(object):
|
|||||||
item.add_tag(tag.handle)
|
item.add_tag(tag.handle)
|
||||||
commit_func(item, trans)
|
commit_func(item, trans)
|
||||||
|
|
||||||
def _to_dot_format(field):
|
|
||||||
"""
|
|
||||||
Convert a field keyword arg into a proper
|
|
||||||
dotted field name.
|
|
||||||
"""
|
|
||||||
return field.replace("__", ".")
|
|
||||||
|
|
||||||
def _select_field_operator_value(field, op, value):
|
|
||||||
"""
|
|
||||||
Convert a field keyword arg into proper
|
|
||||||
field, op, and value.
|
|
||||||
"""
|
|
||||||
alias = {
|
|
||||||
"LT": "<",
|
|
||||||
"GT": ">",
|
|
||||||
"LTE": "<=",
|
|
||||||
"GTE": ">=",
|
|
||||||
"IS_NOT": "IS NOT",
|
|
||||||
"IS_NULL": "IS NULL",
|
|
||||||
"IS_NOT_NULL": "IS NOT NULL",
|
|
||||||
"NE": "<>",
|
|
||||||
}
|
|
||||||
for operator in ["LIKE", "IN"] + list(alias.keys()):
|
|
||||||
operator = "__" + operator
|
|
||||||
if field.endswith(operator):
|
|
||||||
op = field[-len(operator) + 2:]
|
|
||||||
field = field[:-len(operator)]
|
|
||||||
op = alias.get(op, op)
|
|
||||||
field = _to_dot_format(field)
|
|
||||||
return (field, op, value)
|
|
||||||
|
102
gramps/gen/db/test/test_where.py
Normal file
102
gramps/gen/db/test/test_where.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#
|
||||||
|
# Gramps - a GTK+/GNOME based genealogy program
|
||||||
|
#
|
||||||
|
# Copyright (C) 2016 Gramps Development Team
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
|
||||||
|
from gramps.gen.db.where import eval_where
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Tests:
|
||||||
|
|
||||||
|
def make_closure(surname):
|
||||||
|
"""
|
||||||
|
Test closure.
|
||||||
|
"""
|
||||||
|
from gramps.gen.lib import Person
|
||||||
|
return (lambda person:
|
||||||
|
(person.primary_name.surname_list[0].surname == surname and
|
||||||
|
person.gender == Person.MALE))
|
||||||
|
|
||||||
|
class Thing(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.list = ["I0", "I1", "I2"]
|
||||||
|
|
||||||
|
def where(self):
|
||||||
|
return lambda person: person.gramps_id == self.list[1]
|
||||||
|
|
||||||
|
class ClosureTest(unittest.TestCase):
|
||||||
|
def check(self, test):
|
||||||
|
result = eval_where(test[0])
|
||||||
|
self.assertTrue(result == test[1], "%s is not %s" % (result, test[1]))
|
||||||
|
|
||||||
|
def test_01(self):
|
||||||
|
self.check(
|
||||||
|
(lambda family: (family.private and
|
||||||
|
family.mother_handle.gramps_id != "I0001"),
|
||||||
|
['AND', [['private', '==', True],
|
||||||
|
['mother_handle.gramps_id', '!=', 'I0001']]]))
|
||||||
|
|
||||||
|
def test_02(self):
|
||||||
|
self.check(
|
||||||
|
(lambda person: LIKE(person.gramps_id, "I0001"),
|
||||||
|
['gramps_id', 'LIKE', 'I0001']))
|
||||||
|
|
||||||
|
def test_03(self):
|
||||||
|
self.check(
|
||||||
|
(lambda note: note.gramps_id == "N0001",
|
||||||
|
['gramps_id', '==', 'N0001']))
|
||||||
|
|
||||||
|
def test_04(self):
|
||||||
|
self.check(
|
||||||
|
(lambda person: person.event_ref_list.ref.gramps_id == "E0001",
|
||||||
|
['event_ref_list.ref.gramps_id', '==', 'E0001']))
|
||||||
|
|
||||||
|
def test_05(self):
|
||||||
|
self.check(
|
||||||
|
(lambda person: LIKE(person.gramps_id, "I0001") or person.private,
|
||||||
|
["OR", [['gramps_id', 'LIKE', 'I0001'],
|
||||||
|
["private", "==", True]]]))
|
||||||
|
|
||||||
|
def test_06(self):
|
||||||
|
self.check(
|
||||||
|
(lambda person: person.event_ref_list <= 0,
|
||||||
|
["event_ref_list", "<=", 0]))
|
||||||
|
|
||||||
|
def test_07(self):
|
||||||
|
self.check(
|
||||||
|
(lambda person: person.primary_name.surname_list[0].surname == "Smith",
|
||||||
|
["primary_name.surname_list.0.surname", "==", "Smith"]))
|
||||||
|
|
||||||
|
def test_08(self):
|
||||||
|
self.check(
|
||||||
|
(make_closure("Smith"),
|
||||||
|
["AND", [["primary_name.surname_list.0.surname", "==", "Smith"],
|
||||||
|
["gender", "==", 1]]]))
|
||||||
|
|
||||||
|
def test_09(self):
|
||||||
|
self.check(
|
||||||
|
[Thing().where(), ["gramps_id", "==", "I1"]])
|
||||||
|
|
||||||
|
def test_10(self):
|
||||||
|
self.check(
|
||||||
|
(lambda person: LIKE(person.gramps_id, "I000%"),
|
||||||
|
["gramps_id", "LIKE", "I000%"]))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
151
gramps/gen/db/where.py
Normal file
151
gramps/gen/db/where.py
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#
|
||||||
|
# Gramps - a GTK+/GNOME based genealogy program
|
||||||
|
#
|
||||||
|
# Copyright (C) 2016 Gramps Development Team
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
|
||||||
|
from meta.asttools import Visitor
|
||||||
|
from meta.decompiler import _ast, decompile_func
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
|
class ParseFilter(Visitor):
|
||||||
|
def visitName(self, node):
|
||||||
|
return node.id
|
||||||
|
|
||||||
|
def visitNum(self, node):
|
||||||
|
return node.n
|
||||||
|
|
||||||
|
def visitlong(self, node):
|
||||||
|
return node
|
||||||
|
|
||||||
|
def process_expression(self, expr):
|
||||||
|
if isinstance(expr, str):
|
||||||
|
# boolean
|
||||||
|
return [self.process_field(expr), "==", True]
|
||||||
|
elif len(expr) == 3:
|
||||||
|
# (field, op, value)
|
||||||
|
return [self.process_field(expr[0]),
|
||||||
|
expr[1],
|
||||||
|
self.process_value(expr[2])]
|
||||||
|
else:
|
||||||
|
# list of exprs
|
||||||
|
return [self.process_expression(exp) for
|
||||||
|
exp in expr]
|
||||||
|
|
||||||
|
def process_value(self, value):
|
||||||
|
try:
|
||||||
|
return eval(value, self.env)
|
||||||
|
except:
|
||||||
|
return value
|
||||||
|
|
||||||
|
def process_field(self, field):
|
||||||
|
field = field.replace("[", ".").replace("]", "")
|
||||||
|
if field.startswith(self.parameter + "."):
|
||||||
|
return field[len(self.parameter) + 1:]
|
||||||
|
else:
|
||||||
|
return field
|
||||||
|
|
||||||
|
def visitCall(self, node):
|
||||||
|
"""
|
||||||
|
Handle LIKE()
|
||||||
|
"""
|
||||||
|
return [self.process_field(self.visit(node.args[0])),
|
||||||
|
self.visit(node.func),
|
||||||
|
self.process_value(self.visit(node.args[1]))]
|
||||||
|
|
||||||
|
def visitStr(self, node):
|
||||||
|
return node.s
|
||||||
|
|
||||||
|
def visitlist(self, list):
|
||||||
|
return [self.visit(node) for node in list]
|
||||||
|
|
||||||
|
def visitCompare(self, node):
|
||||||
|
return [self.process_field(self.visit(node.left)),
|
||||||
|
" ".join(self.visit(node.ops)),
|
||||||
|
self.process_value(self.visit(node.comparators[0]))]
|
||||||
|
|
||||||
|
def visitAttribute(self, node):
|
||||||
|
return "%s.%s" % (self.visit(node.value), node.attr)
|
||||||
|
|
||||||
|
def get_boolean_op(self, node):
|
||||||
|
if isinstance(node, _ast.And):
|
||||||
|
return "AND"
|
||||||
|
elif isinstance(node, _ast.Or):
|
||||||
|
return "OR"
|
||||||
|
else:
|
||||||
|
raise Exception("invalid boolean")
|
||||||
|
|
||||||
|
def visitNotEq(self, node):
|
||||||
|
return "!="
|
||||||
|
|
||||||
|
def visitLtE(self, node):
|
||||||
|
return "<="
|
||||||
|
|
||||||
|
def visitGtE(self, node):
|
||||||
|
return ">="
|
||||||
|
|
||||||
|
def visitEq(self, node):
|
||||||
|
return "=="
|
||||||
|
|
||||||
|
def visitBoolOp(self, node):
|
||||||
|
"""
|
||||||
|
BoolOp: boolean operator
|
||||||
|
"""
|
||||||
|
op = self.get_boolean_op(node.op)
|
||||||
|
values = list(node.values)
|
||||||
|
return [op, self.process_expression(
|
||||||
|
[self.visit(value) for value in values])]
|
||||||
|
|
||||||
|
def visitLambda(self, node):
|
||||||
|
self.parameter = self.visit(node.args)[0]
|
||||||
|
return self.visit(node.body)
|
||||||
|
|
||||||
|
def visitarguments(self, node):
|
||||||
|
return [self.visit(arg) for arg in node.args]
|
||||||
|
|
||||||
|
def visitarg(self, node):
|
||||||
|
return node.arg
|
||||||
|
|
||||||
|
def visitSubscript(self, node):
|
||||||
|
return "%s[%s]" % (self.visit(node.value),
|
||||||
|
self.visit(node.slice))
|
||||||
|
|
||||||
|
def visitIndex(self, node):
|
||||||
|
return self.visit(node.value)
|
||||||
|
|
||||||
|
def make_env(closure):
|
||||||
|
"""
|
||||||
|
Create an environment from the closure.
|
||||||
|
"""
|
||||||
|
env = copy.copy(closure.__globals__)
|
||||||
|
if closure.__closure__:
|
||||||
|
for i in range(len(closure.__closure__)):
|
||||||
|
env[closure.__code__.co_freevars[i]] = closure.__closure__[i].cell_contents
|
||||||
|
return env
|
||||||
|
|
||||||
|
def eval_where(closure):
|
||||||
|
"""
|
||||||
|
Given a closure, parse and evaluate it.
|
||||||
|
Return a WHERE expression.
|
||||||
|
"""
|
||||||
|
parser = ParseFilter()
|
||||||
|
parser.env = make_env(closure)
|
||||||
|
ast_top = decompile_func(closure)
|
||||||
|
result = parser.visit(ast_top)
|
||||||
|
return result
|
||||||
|
|
@ -123,12 +123,12 @@ class BSDDBTest(unittest.TestCase):
|
|||||||
self.assertTrue(len(result) == 60, len(result))
|
self.assertTrue(len(result) == 60, len(result))
|
||||||
|
|
||||||
def test_queryset_2(self):
|
def test_queryset_2(self):
|
||||||
result = list(self.db.Person.filter(gramps_id__LIKE="I000%").select())
|
result = list(self.db.Person.where(lambda person: LIKE(person.gramps_id, "I000%")).select())
|
||||||
self.assertTrue(len(result) == 10, len(result))
|
self.assertTrue(len(result) == 10, len(result))
|
||||||
|
|
||||||
def test_queryset_3(self):
|
def test_queryset_3(self):
|
||||||
result = list(self.db.Family
|
result = list(self.db.Family
|
||||||
.filter(mother_handle__gramps_id__LIKE="I003%")
|
.where(lambda family: LIKE(family.mother_handle.gramps_id, "I003%"))
|
||||||
.select())
|
.select())
|
||||||
self.assertTrue(len(result) == 6, result)
|
self.assertTrue(len(result) == 6, result)
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ class BSDDBTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_queryset_4b(self):
|
def test_queryset_4b(self):
|
||||||
result = list(self.db.Family
|
result = list(self.db.Family
|
||||||
.filter(mother_handle__event_ref_list__ref__gramps_id='E0156')
|
.where(lambda family: family.mother_handle.event_ref_list.ref.gramps_id == 'E0156')
|
||||||
.select())
|
.select())
|
||||||
self.assertTrue(len(result) == 1, len(result))
|
self.assertTrue(len(result) == 1, len(result))
|
||||||
|
|
||||||
@ -154,9 +154,8 @@ class BSDDBTest(unittest.TestCase):
|
|||||||
[r["mother_handle.event_ref_list.0"] for r in result])
|
[r["mother_handle.event_ref_list.0"] for r in result])
|
||||||
|
|
||||||
def test_queryset_7(self):
|
def test_queryset_7(self):
|
||||||
from gramps.gen.db import NOT
|
|
||||||
result = list(self.db.Family
|
result = list(self.db.Family
|
||||||
.filter(NOT(mother_handle__event_ref_list__0=None))
|
.where(lambda family: family.mother_handle.event_ref_list[0] != None)
|
||||||
.select())
|
.select())
|
||||||
self.assertTrue(len(result) == 21, len(result))
|
self.assertTrue(len(result) == 21, len(result))
|
||||||
|
|
||||||
@ -188,22 +187,28 @@ class BSDDBTest(unittest.TestCase):
|
|||||||
self.assertTrue(result == 60, result)
|
self.assertTrue(result == 60, result)
|
||||||
|
|
||||||
def test_tag_1(self):
|
def test_tag_1(self):
|
||||||
self.db.Person.filter(gramps_id="I0001").tag("Test")
|
self.db.Person.where(lambda person: person.gramps_id == "I0001").tag("Test")
|
||||||
result = self.db.Person.filter(tag_list__name="Test").count()
|
result = self.db.Person.where(lambda person: person.tag_list.name == "Test").count()
|
||||||
self.assertTrue(result == 1, result)
|
self.assertTrue(result == 1, result)
|
||||||
|
|
||||||
# def test_filter_1(self):
|
def test_filter_1(self):
|
||||||
# from gramps.gen.filters.rules.person import (IsDescendantOf,
|
from gramps.gen.filters.rules.person import (IsDescendantOf,
|
||||||
# IsAncestorOf)
|
IsAncestorOf)
|
||||||
# from gramps.gen.filters import GenericFilter
|
from gramps.gen.filters import GenericFilter
|
||||||
# filter = GenericFilter()
|
filter = GenericFilter()
|
||||||
# filter.set_logical_op("or")
|
filter.set_logical_op("or")
|
||||||
# filter.add_rule(IsDescendantOf([self.db.get_default_person().gramps_id,
|
filter.add_rule(IsDescendantOf([self.db.get_default_person().gramps_id,
|
||||||
# True]))
|
True]))
|
||||||
# filter.add_rule(IsAncestorOf([self.db.get_default_person().gramps_id,
|
filter.add_rule(IsAncestorOf([self.db.get_default_person().gramps_id,
|
||||||
# True]))
|
True]))
|
||||||
# result = self.db.Person.filter(filter).count()
|
result = self.db.Person.filter(filter).count()
|
||||||
# self.assertTrue(result == 15, result)
|
self.assertTrue(result == 15, result)
|
||||||
|
filter.where = lambda person: person.private == True
|
||||||
|
result = self.db.Person.filter(filter).count()
|
||||||
|
self.assertTrue(result == 1, result)
|
||||||
|
filter.where = lambda person: person.private != True
|
||||||
|
result = self.db.Person.filter(filter).count()
|
||||||
|
self.assertTrue(result == 14, result)
|
||||||
|
|
||||||
def test_filter_2(self):
|
def test_filter_2(self):
|
||||||
result = self.db.Person.filter(lambda p: p.private).count()
|
result = self.db.Person.filter(lambda p: p.private).count()
|
||||||
|
Loading…
Reference in New Issue
Block a user