Nouveaux opérateurs pour les vues personnalisées (#76758) #283
|
@ -1289,14 +1289,21 @@ def test_api_list_formdata_string_filter(pub, local_user):
|
|||
data_class = formdef.data_class()
|
||||
data_class.wipe()
|
||||
|
||||
for i in range(4):
|
||||
for i in range(5):
|
||||
formdata = data_class()
|
||||
formdata.data = {}
|
||||
if i < 3: # None values for the last one
|
||||
formdata.data = {
|
||||
'0': 'FOO %s' % i,
|
||||
'1': '%s' % (9 + i),
|
||||
}
|
||||
if i == 3:
|
||||
# Empty values
|
||||
formdata.data = {
|
||||
'0': 'FOO %s' % i,
|
||||
'1': '%s' % (9 + i),
|
||||
'0': '',
|
||||
'1': '',
|
||||
}
|
||||
if i == 4:
|
||||
# None values
|
||||
formdata.data = {}
|
||||
formdata.user_id = local_user.id
|
||||
formdata.just_created()
|
||||
formdata.jump_status('new')
|
||||
|
@ -1306,12 +1313,20 @@ def test_api_list_formdata_string_filter(pub, local_user):
|
|||
assert len(resp.json) == 1
|
||||
params = [
|
||||
('eq', 'FOO 2', 1),
|
||||
('ne', 'FOO 2', 3),
|
||||
('lt', 'FOO 2', 2),
|
||||
('lte', 'FOO 2', 3),
|
||||
('ne', 'FOO 2', 4),
|
||||
('lt', 'FOO 2', 3),
|
||||
('lte', 'FOO 2', 4),
|
||||
('gt', 'FOO 2', 0),
|
||||
('gt', '42', 0),
|
||||
('gte', 'FOO 2', 1),
|
||||
('in', 'FOO 2', 1),
|
||||
('in', 'FOO 2|FOO 1', 2),
|
||||
('not_in', 'FOO 2', 3),
|
||||
('not_in', 'FOO 2|FOO 1', 2),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 3),
|
||||
('between', 'FOO 1|FOO 2', 1),
|
||||
('between', 'FOO 2|FOO 1', 1),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1322,15 +1337,34 @@ def test_api_list_formdata_string_filter(pub, local_user):
|
|||
)
|
||||
assert len(resp.json) == result
|
||||
|
||||
resp = get_app(pub).get(
|
||||
sign_uri(
|
||||
'/api/forms/test/list?filter-string=plop&filter-string-operator=between',
|
||||
user=local_user,
|
||||
),
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err_desc'] == 'Invalid value "plop" for operator "between" and filter "filter-string"'
|
||||
|
||||
params = [
|
||||
('eq', '10', 1),
|
||||
('eq', '010', 1),
|
||||
('ne', '10', 3),
|
||||
('lt', '10', 1),
|
||||
('lte', '10', 2),
|
||||
('gt', '10', 1),
|
||||
('gt', '9', 2),
|
||||
('gte', '10', 2),
|
||||
('in', '10', 1),
|
||||
('in', '10|9', 2),
|
||||
('in', '10|42', 1),
|
||||
('in', '10|a', 1),
|
||||
('not_in', '10', 2),
|
||||
('not_in', '10|9', 1),
|
||||
('not_in', '10|42', 2),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 3),
|
||||
('between', '9|10', 1),
|
||||
('between', '10|9', 1),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1373,14 +1407,22 @@ def test_api_list_formdata_item_filter(pub, local_user):
|
|||
data_class = formdef.data_class()
|
||||
data_class.wipe()
|
||||
|
||||
for i in range(4):
|
||||
for i in range(5):
|
||||
formdata = data_class()
|
||||
formdata.data = {}
|
||||
if i < 3: # None values for the last one
|
||||
formdata.data = {
|
||||
'0': str(9 + i),
|
||||
'1': 'foo' if i % 2 else 'bar',
|
||||
}
|
||||
if i == 3:
|
||||
# Empty values
|
||||
formdata.data = {
|
||||
'0': str(9 + i),
|
||||
'1': 'foo' if i % 2 else 'bar',
|
||||
'0': '',
|
||||
'1': '',
|
||||
}
|
||||
if i == 4:
|
||||
# None values
|
||||
formdata.data = {}
|
||||
formdata.user_id = local_user.id
|
||||
formdata.just_created()
|
||||
formdata.jump_status('new')
|
||||
|
@ -1390,13 +1432,23 @@ def test_api_list_formdata_item_filter(pub, local_user):
|
|||
assert len(resp.json) == 1
|
||||
params = [
|
||||
('eq', '10', 1),
|
||||
('eq', '010', 1),
|
||||
('ne', '10', 3),
|
||||
('lt', '10', 1),
|
||||
('lte', '10', 2),
|
||||
('gt', '10', 1),
|
||||
('gt', '9', 2),
|
||||
('gte', '10', 2),
|
||||
('in', '10', 1),
|
||||
('in', '10|9', 2),
|
||||
('in', '10|42', 1),
|
||||
('in', '10|a', 1),
|
||||
('not_in', '10', 2),
|
||||
('not_in', '10|9', 1),
|
||||
('not_in', '10|42', 2),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 3),
|
||||
('between', '9|10', 1),
|
||||
('between', '10|9', 1),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1411,12 +1463,22 @@ def test_api_list_formdata_item_filter(pub, local_user):
|
|||
assert len(resp.json) == 1
|
||||
params = [
|
||||
('eq', 'foo', 1),
|
||||
('ne', 'foo', 3),
|
||||
('lt', 'foo', 2),
|
||||
('lte', 'foo', 3),
|
||||
('ne', 'foo', 4),
|
||||
('lt', 'foo', 3),
|
||||
('lte', 'foo', 4),
|
||||
('gt', 'foo', 0),
|
||||
('gt', '42', 0),
|
||||
('gte', 'foo', 1),
|
||||
('in', 'foo', 1),
|
||||
('in', 'foo|bar', 3),
|
||||
('in', 'foo|baz', 1),
|
||||
('not_in', 'foo', 3),
|
||||
('not_in', 'foo|bar', 1),
|
||||
('not_in', 'foo|42', 3),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 3),
|
||||
('between', 'bar|foo', 2),
|
||||
('between', 'foo|bar', 2),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1475,8 +1537,9 @@ def test_api_list_formdata_item_filter_on_cards(pub, local_user, sql_queries):
|
|||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-item=%s' % carddata.id, user=local_user))
|
||||
assert len(resp.json) == 1
|
||||
carddata_sql_queries = [q for q in sql_queries if 'FROM carddata' in q]
|
||||
assert len(carddata_sql_queries) == 1
|
||||
assert len(carddata_sql_queries) == 2
|
||||
assert ' id = ' in carddata_sql_queries[0]
|
||||
assert ' id = ' in carddata_sql_queries[1]
|
||||
|
||||
|
||||
def test_api_list_formdata_items_filter(pub, local_user):
|
||||
|
@ -1512,14 +1575,22 @@ def test_api_list_formdata_items_filter(pub, local_user):
|
|||
data_class = formdef.data_class()
|
||||
data_class.wipe()
|
||||
|
||||
for i in range(4):
|
||||
for i in range(5):
|
||||
formdata = data_class()
|
||||
formdata.data = {}
|
||||
if i < 3: # None values for the last one
|
||||
formdata.data = {
|
||||
'0': ['9000' if i % 2 else '11000', '10000'],
|
||||
'1': ['foo' if i % 2 else 'bar', 'baz'],
|
||||
}
|
||||
if i == 3:
|
||||
# Empty values
|
||||
formdata.data = {
|
||||
'0': ['9000' if i % 2 else '11000', '10000'],
|
||||
'1': ['foo' if i % 2 else 'bar', 'baz'],
|
||||
'0': [],
|
||||
'1': [],
|
||||
}
|
||||
if i == 4:
|
||||
# None values
|
||||
formdata.data = {}
|
||||
formdata.user_id = local_user.id
|
||||
formdata.just_created()
|
||||
formdata.jump_status('new')
|
||||
|
@ -1529,15 +1600,26 @@ def test_api_list_formdata_items_filter(pub, local_user):
|
|||
assert len(resp.json) == 2
|
||||
params = [
|
||||
('eq', '11000', 2),
|
||||
('eq', '011000', 2),
|
||||
('eq', '10000', 3),
|
||||
('ne', '9000', 3),
|
||||
('ne', '10000', 1),
|
||||
('ne', '9000', 4),
|
||||
('ne', '10000', 2),
|
||||
('lt', '10000', 1),
|
||||
('lte', '10000', 3),
|
||||
('gt', '10000', 2),
|
||||
('gt', '9000', 3),
|
||||
('gte', '11000', 2),
|
||||
('in', '11000', 2),
|
||||
('in', '11000|10000', 3),
|
||||
('in', '11000|9000', 3),
|
||||
('not_in', '11000', 3),
|
||||
('not_in', '11000|10000', 2),
|
||||
('not_in', '11000|9000', 2),
|
||||
('not_in', '11001|9000', 4),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 3),
|
||||
('between', '9000|10000', 1),
|
||||
('between', '10000|9000', 1),
|
||||
('between', '9000|9001', 1),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1550,12 +1632,20 @@ def test_api_list_formdata_items_filter(pub, local_user):
|
|||
|
||||
params = [
|
||||
('eq', 'foo', 1),
|
||||
('ne', 'foo', 3),
|
||||
('ne', 'foo', 4),
|
||||
('lt', 'foo', 3),
|
||||
('lte', 'foo', 3),
|
||||
('gt', 'foo', 0),
|
||||
('gt', '42', 0),
|
||||
('gte', 'foo', 1),
|
||||
('in', 'foo', 1),
|
||||
('in', 'foo|bar', 3),
|
||||
('not_in', 'foo', 4),
|
||||
('not_in', 'foo|bar', 2),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 3),
|
||||
('between', 'bar|bazz', 3),
|
||||
('between', 'bazz|bar', 3),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1607,6 +1697,8 @@ def test_api_list_formdata_bool_filter(pub, local_user):
|
|||
('eq', 'true', 1),
|
||||
('ne', 'true', 3),
|
||||
('ne', 'false', 2),
|
||||
('absent', 'on', 1),
|
||||
('existing', 'on', 3),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1616,7 +1708,7 @@ def test_api_list_formdata_bool_filter(pub, local_user):
|
|||
)
|
||||
)
|
||||
assert len(resp.json) == result
|
||||
for operator in ['lt', 'lte', 'gt', 'gte']:
|
||||
for operator in ['lt', 'lte', 'gt', 'gte', 'in', 'not_in', 'between']:
|
||||
resp = get_app(pub).get(
|
||||
sign_uri(
|
||||
'/api/forms/test/list?filter-bool=true&filter-bool-operator=%s' % operator, user=local_user
|
||||
|
@ -1659,22 +1751,30 @@ def test_api_list_formdata_date_filter(pub, local_user):
|
|||
for value in ['2021-06-11', '11/06/2021']:
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-date=%s' % value, user=local_user))
|
||||
assert len(resp.json) == 1
|
||||
params = [
|
||||
('eq', 1),
|
||||
('ne', 3),
|
||||
('lt', 1),
|
||||
('lte', 2),
|
||||
('gt', 1),
|
||||
('gte', 2),
|
||||
]
|
||||
for operator, result in params:
|
||||
resp = get_app(pub).get(
|
||||
sign_uri(
|
||||
'/api/forms/test/list?filter-date=%s&filter-date-operator=%s' % (value, operator),
|
||||
user=local_user,
|
||||
)
|
||||
params = [
|
||||
('eq', '2021-06-11', 1),
|
||||
('ne', '2021-06-11', 3),
|
||||
('lt', '2021-06-11', 1),
|
||||
('lte', '2021-06-11', 2),
|
||||
('gt', '2021-06-11', 1),
|
||||
('gte', '2021-06-11', 2),
|
||||
('in', '2021-06-12', 1),
|
||||
('in', '2021-06-12|2021-06-15', 1),
|
||||
('not_in', '2021-06-12', 2),
|
||||
('not_in', '2021-06-12|2021-06-15', 2),
|
||||
('absent', 'on', 1),
|
||||
('existing', 'on', 3),
|
||||
('between', '2021-06-12|2021-06-15', 1),
|
||||
('between', '2021-06-15|2021-06-12', 1),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
sign_uri(
|
||||
'/api/forms/test/list?filter-date=%s&filter-date-operator=%s' % (value, operator),
|
||||
user=local_user,
|
||||
)
|
||||
assert len(resp.json) == result
|
||||
)
|
||||
assert len(resp.json) == result
|
||||
|
||||
|
||||
def test_api_list_formdata_email_filter(pub, local_user):
|
||||
|
@ -1697,11 +1797,18 @@ def test_api_list_formdata_email_filter(pub, local_user):
|
|||
data_class = formdef.data_class()
|
||||
data_class.wipe()
|
||||
|
||||
for i in range(4):
|
||||
for i in range(5):
|
||||
formdata = data_class()
|
||||
formdata.data = {}
|
||||
if i < 3: # None values for the last one
|
||||
formdata.data = {'0': 'a@localhost' if i % 2 else 'b@localhost'}
|
||||
formdata.data = {'0': 'a@localhost' if i % 2 else 'b@localhost'}
|
||||
if i == 3:
|
||||
# Empty values
|
||||
formdata.data = {
|
||||
'0': '',
|
||||
}
|
||||
if i == 4:
|
||||
# None values
|
||||
formdata.data = {}
|
||||
formdata.user_id = local_user.id
|
||||
formdata.just_created()
|
||||
formdata.jump_status('new')
|
||||
|
@ -1711,7 +1818,13 @@ def test_api_list_formdata_email_filter(pub, local_user):
|
|||
assert len(resp.json) == 1
|
||||
params = [
|
||||
('eq', 'a@localhost', 1),
|
||||
('ne', 'a@localhost', 3),
|
||||
('ne', 'a@localhost', 4),
|
||||
('in', 'a@localhost', 1),
|
||||
('in', 'a@localhost|b@localhost', 3),
|
||||
('not_in', 'a@localhost', 3),
|
||||
('not_in', 'a@localhost|b@localhost', 1),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 3),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1721,7 +1834,7 @@ def test_api_list_formdata_email_filter(pub, local_user):
|
|||
)
|
||||
)
|
||||
assert len(resp.json) == result
|
||||
for operator in ['lt', 'lte', 'gt', 'gte']:
|
||||
for operator in ['lt', 'lte', 'gt', 'gte', 'between']:
|
||||
resp = get_app(pub).get(
|
||||
sign_uri(
|
||||
'/api/forms/test/list?filter-email=a@localhost&filter-email-operator=%s' % operator,
|
||||
|
@ -1767,7 +1880,6 @@ def test_api_list_formdata_internal_id_filter(pub, local_user):
|
|||
|
||||
params = [
|
||||
('eq', '1', 1),
|
||||
('eq', '01', 1),
|
||||
('ne', '1', 10),
|
||||
('lt', '1', 0),
|
||||
('lte', '1', 1),
|
||||
|
@ -1789,6 +1901,16 @@ def test_api_list_formdata_internal_id_filter(pub, local_user):
|
|||
)
|
||||
assert resp.json['err_desc'] == 'Invalid value "blabla" for "filter-internal-id-value"'
|
||||
|
||||
for operator in ['in', 'not_in', 'absent', 'existing', 'between']:
|
||||
resp = get_app(pub).get(
|
||||
sign_uri(
|
||||
'/api/forms/test/list?filter-internal-id=42&filter-internal-id-operator=%s' % operator,
|
||||
user=local_user,
|
||||
),
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err_desc'] == 'Invalid operator "%s" for "filter-internal-id"' % operator
|
||||
|
||||
# multi-ids
|
||||
resp = get_app(pub).get(
|
||||
sign_uri(
|
||||
|
@ -1908,38 +2030,68 @@ def test_api_list_formdata_block_field_filter(pub, local_user):
|
|||
data_class = formdef.data_class()
|
||||
data_class.wipe()
|
||||
|
||||
for i in range(11):
|
||||
for i in range(14):
|
||||
formdata = data_class()
|
||||
if i < 10: # None values for the last one
|
||||
formdata.data = {
|
||||
'0': {
|
||||
'data': [
|
||||
{
|
||||
'1': 'plop%s' % i,
|
||||
'2': '1' if i % 2 else '2',
|
||||
'2_display': 'foo' if i % 2 else 'bar',
|
||||
'2_structured': 'XXX' if i % 2 else 'YYY',
|
||||
'3': bool(i % 2),
|
||||
'4': '2021-06-%02d' % (i + 1),
|
||||
'5': 'a@localhost' if i % 2 else 'b@localhost',
|
||||
},
|
||||
],
|
||||
'schema': {}, # not important here
|
||||
},
|
||||
'0_display': 'hello',
|
||||
}
|
||||
if i == 0:
|
||||
# 2 elements with values
|
||||
formdata.data['0']['data'].append(
|
||||
{
|
||||
'1': 'plop%s' % (i + 1),
|
||||
'2': '1',
|
||||
'2_display': 'foo',
|
||||
'2_structured': 'XXX',
|
||||
'3': True,
|
||||
'4': '2021-06-02',
|
||||
'5': 'a@localhost',
|
||||
},
|
||||
)
|
||||
if i == 10:
|
||||
# 2 elements, the second without values
|
||||
formdata.data['0']['data'].append(
|
||||
{
|
||||
'1': '',
|
||||
'2': '',
|
||||
'4': '',
|
||||
'5': '',
|
||||
}
|
||||
)
|
||||
if i == 11:
|
||||
# 2 elements, the second with non values
|
||||
formdata.data['0']['data'].append({})
|
||||
if i == 12:
|
||||
# only one element, without values
|
||||
formdata.data = {
|
||||
'0': {
|
||||
'data': [
|
||||
{
|
||||
'1': 'plop%s' % i,
|
||||
'2': '1' if i % 2 else '2',
|
||||
'2_display': 'foo' if i % 2 else 'bar',
|
||||
'2_structured': 'XXX' if i % 2 else 'YYY',
|
||||
'3': bool(i % 2),
|
||||
'4': '2021-06-%02d' % (i + 1),
|
||||
'5': 'a@localhost' if i % 2 else 'b@localhost',
|
||||
},
|
||||
],
|
||||
'schema': {}, # not important here
|
||||
},
|
||||
'0_display': 'hello',
|
||||
'1': '',
|
||||
'2': '',
|
||||
'4': '',
|
||||
'5': '',
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
if i == 0:
|
||||
formdata.data['0']['data'].append(
|
||||
{
|
||||
'1': 'plop%s' % (i + 1),
|
||||
'2': '1',
|
||||
'2_display': 'foo',
|
||||
'2_structured': 'XXX',
|
||||
'3': True,
|
||||
'4': '2021-06-02',
|
||||
'5': 'a@localhost',
|
||||
},
|
||||
)
|
||||
if i == 13:
|
||||
# no element
|
||||
formdata.data = {}
|
||||
formdata.user_id = local_user.id
|
||||
formdata.just_created()
|
||||
formdata.jump_status('new')
|
||||
|
@ -1950,17 +2102,25 @@ def test_api_list_formdata_block_field_filter(pub, local_user):
|
|||
assert len(resp.json) == 1
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-blockdata_string=plop2', user=local_user))
|
||||
assert len(resp.json) == 1
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-blockdata_string=plop10', user=local_user))
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-blockdata_string=plop12', user=local_user))
|
||||
assert len(resp.json) == 0
|
||||
params = [
|
||||
('eq', 'plop5', 1),
|
||||
('ne', 'plop5', 10),
|
||||
('ne', 'plop1', 9),
|
||||
('lt', 'plop5', 5),
|
||||
('lte', 'plop5', 6),
|
||||
('ne', 'plop5', 13),
|
||||
('ne', 'plop1', 12),
|
||||
('lt', 'plop5', 8),
|
||||
('lte', 'plop5', 9),
|
||||
('gt', 'plop5', 4),
|
||||
('gt', '42', 0),
|
||||
('gte', 'plop5', 5),
|
||||
('in', 'plop5', 1),
|
||||
('in', 'plop5|plop4', 2),
|
||||
('not_in', 'plop5', 13),
|
||||
('not_in', 'plop5|plop4', 12),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 12),
|
||||
('between', 'plop1|plop5', 7),
|
||||
('between', 'plop5|plop1', 7),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1973,18 +2133,30 @@ def test_api_list_formdata_block_field_filter(pub, local_user):
|
|||
assert len(resp.json) == result
|
||||
# item
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-blockdata_item=1', user=local_user))
|
||||
assert len(resp.json) == 6
|
||||
assert len(resp.json) == 7
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-blockdata_item=2', user=local_user))
|
||||
assert len(resp.json) == 5
|
||||
assert len(resp.json) == 6
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-blockdata_item=3', user=local_user))
|
||||
assert len(resp.json) == 0
|
||||
params = [
|
||||
('eq', '1', 6),
|
||||
('ne', '1', 5),
|
||||
('lt', '2', 6),
|
||||
('lte', '1', 6),
|
||||
('gt', '1', 5),
|
||||
('gte', '2', 5),
|
||||
('eq', '1', 7),
|
||||
('ne', '1', 7),
|
||||
('lt', '2', 7),
|
||||
('lte', '1', 7),
|
||||
('gt', '1', 6),
|
||||
('gte', '2', 6),
|
||||
('in', '1', 7),
|
||||
('in', '1|2', 12),
|
||||
('in', '1|42', 7),
|
||||
('in', '1|a', 7),
|
||||
('not_in', '1', 7),
|
||||
('not_in', '1|2', 2),
|
||||
('not_in', '1|42', 7),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 12),
|
||||
('between', '1|2', 7),
|
||||
('between', '1|3', 12),
|
||||
('between', '3|1', 12),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -1997,16 +2169,18 @@ def test_api_list_formdata_block_field_filter(pub, local_user):
|
|||
assert len(resp.json) == result
|
||||
# bool
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-blockdata_bool=true', user=local_user))
|
||||
assert len(resp.json) == 6
|
||||
assert len(resp.json) == 7
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?filter-blockdata_bool=false', user=local_user))
|
||||
assert len(resp.json) == 5
|
||||
assert len(resp.json) == 6
|
||||
resp = get_app(pub).get(
|
||||
sign_uri('/api/forms/test/list?filter-blockdata_bool=foobar', user=local_user), status=400
|
||||
)
|
||||
assert resp.json['err_desc'] == 'Invalid value "foobar" for "filter-blockdata_bool"'
|
||||
params = [
|
||||
('eq', 'true', 6),
|
||||
('ne', 'true', 5),
|
||||
('eq', 'true', 7),
|
||||
('ne', 'true', 7),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 12),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -2017,7 +2191,7 @@ def test_api_list_formdata_block_field_filter(pub, local_user):
|
|||
)
|
||||
)
|
||||
assert len(resp.json) == result
|
||||
for operator in ['lt', 'lte', 'gt', 'gte']:
|
||||
for operator in ['lt', 'lte', 'gt', 'gte', 'in', 'not_in', 'between']:
|
||||
resp = get_app(pub).get(
|
||||
sign_uri(
|
||||
'/api/forms/test/list?filter-blockdata_bool=true&filter-blockdata_bool-operator=%s'
|
||||
|
@ -2042,11 +2216,19 @@ def test_api_list_formdata_block_field_filter(pub, local_user):
|
|||
assert len(resp.json) == 2
|
||||
params = [
|
||||
('eq', '2021-06-02', 2),
|
||||
('ne', '2021-06-02', 9),
|
||||
('lt', '2021-06-02', 1),
|
||||
('lte', '2021-06-02', 2),
|
||||
('gt', '2021-06-02', 8),
|
||||
('gte', '2021-06-02', 10),
|
||||
('ne', '2021-06-02', 12),
|
||||
('lt', '2021-06-02', 3),
|
||||
('lte', '2021-06-02', 4),
|
||||
('gt', '2021-06-02', 10),
|
||||
('gte', '2021-06-02', 12),
|
||||
('in', '2021-06-02', 2),
|
||||
('in', '2021-06-02|2021-06-05', 3),
|
||||
('not_in', '2021-06-02', 12),
|
||||
('not_in', '2021-06-02|2021-06-05', 11),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 12),
|
||||
('between', '2021-06-02|2021-06-05', 4),
|
||||
('between', '2021-06-05|2021-06-02', 4),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -2061,18 +2243,24 @@ def test_api_list_formdata_block_field_filter(pub, local_user):
|
|||
resp = get_app(pub).get(
|
||||
sign_uri('/api/forms/test/list?filter-blockdata_email=a@localhost', user=local_user)
|
||||
)
|
||||
assert len(resp.json) == 6
|
||||
assert len(resp.json) == 7
|
||||
resp = get_app(pub).get(
|
||||
sign_uri('/api/forms/test/list?filter-blockdata_email=b@localhost', user=local_user)
|
||||
)
|
||||
assert len(resp.json) == 5
|
||||
assert len(resp.json) == 6
|
||||
resp = get_app(pub).get(
|
||||
sign_uri('/api/forms/test/list?filter-blockdata_email=c@localhost', user=local_user)
|
||||
)
|
||||
assert len(resp.json) == 0
|
||||
params = [
|
||||
('eq', 'a@localhost', 6),
|
||||
('ne', 'a@localhost', 5),
|
||||
('eq', 'a@localhost', 7),
|
||||
('ne', 'a@localhost', 7),
|
||||
('in', 'a@localhost', 7),
|
||||
('in', 'a@localhost|b@localhost', 12),
|
||||
('not_in', 'a@localhost', 7),
|
||||
('not_in', 'a@localhost|b@localhost', 2),
|
||||
('absent', 'on', 2),
|
||||
('existing', 'on', 12),
|
||||
]
|
||||
for operator, value, result in params:
|
||||
resp = get_app(pub).get(
|
||||
|
@ -2083,7 +2271,7 @@ def test_api_list_formdata_block_field_filter(pub, local_user):
|
|||
)
|
||||
)
|
||||
assert len(resp.json) == result
|
||||
for operator in ['lt', 'lte', 'gt', 'gte']:
|
||||
for operator in ['lt', 'lte', 'gt', 'gte', 'between']:
|
||||
resp = get_app(pub).get(
|
||||
sign_uri(
|
||||
'/api/forms/test/list?filter-blockdata_email=plop0&filter-blockdata_email-operator=%s'
|
||||
|
@ -2125,21 +2313,24 @@ def test_api_list_formdata_block_field_filter(pub, local_user):
|
|||
return None
|
||||
return d['fields']['blockdata_raw'][0]['string']
|
||||
|
||||
plop_list = ['', 'plop0', 'plop1', 'plop10', 'plop11'] + ['plop%s' % i for i in range(2, 10)] + [None]
|
||||
reversed_plop_list = list(reversed(plop_list))
|
||||
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on&order_by=f0-1', user=local_user))
|
||||
assert [get_string(d) for d in resp.json] == ['plop%s' % i for i in range(0, 10)] + [None]
|
||||
assert [get_string(d) for d in resp.json] == plop_list
|
||||
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/list?full=on&order_by=-f0-1', user=local_user))
|
||||
assert [get_string(d) for d in resp.json] == [None] + ['plop%s' % i for i in range(9, -1, -1)]
|
||||
assert [get_string(d) for d in resp.json] == reversed_plop_list
|
||||
|
||||
resp = get_app(pub).get(
|
||||
sign_uri('/api/forms/test/list?full=on&order_by=blockdata_string', user=local_user)
|
||||
)
|
||||
assert [get_string(d) for d in resp.json] == ['plop%s' % i for i in range(0, 10)] + [None]
|
||||
assert [get_string(d) for d in resp.json] == plop_list
|
||||
|
||||
resp = get_app(pub).get(
|
||||
sign_uri('/api/forms/test/list?full=on&order_by=-blockdata_string', user=local_user)
|
||||
)
|
||||
assert [get_string(d) for d in resp.json] == [None] + ['plop%s' % i for i in range(9, -1, -1)]
|
||||
assert [get_string(d) for d in resp.json] == reversed_plop_list
|
||||
|
||||
|
||||
def test_api_anonymized_formdata(pub, local_user, admin_user):
|
||||
|
|
|
@ -251,6 +251,8 @@ def test_backoffice_bool_filter(pub):
|
|||
assert [x[0] for x in resp.forms['listing-settings']['filter-%s-operator' % field.id].options] == [
|
||||
'eq',
|
||||
'ne',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
|
||||
resp.forms['listing-settings']['filter-%s-value' % field.id].value = 'true'
|
||||
|
@ -330,6 +332,11 @@ def test_backoffice_item_filter(pub):
|
|||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'between',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
|
||||
resp.forms['listing-settings']['filter-4-value'].value = 'â'
|
||||
|
@ -529,6 +536,11 @@ def test_backoffice_bofield_item_filter(pub):
|
|||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'between',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
|
||||
resp.forms['listing-settings']['filter-bo0-1-value'].value = 'â'
|
||||
|
@ -631,6 +643,11 @@ def test_backoffice_items_filter(pub):
|
|||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'between',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
|
||||
assert [x[2] for x in resp.forms['listing-settings']['filter-4-value'].options] == ['', 'â', 'b', 'd']
|
||||
|
@ -792,6 +809,11 @@ def test_backoffice_string_filter(pub):
|
|||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'between',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
|
||||
resp.forms['listing-settings']['filter-4-value'].value = 'a'
|
||||
|
@ -899,7 +921,14 @@ def test_backoffice_email_filter(pub):
|
|||
|
||||
assert resp.forms['listing-settings']['filter-4-value'].value == ''
|
||||
assert resp.forms['listing-settings']['filter-4-operator'].value == 'eq'
|
||||
assert [x[0] for x in resp.forms['listing-settings']['filter-4-operator'].options] == ['eq', 'ne']
|
||||
assert [x[0] for x in resp.forms['listing-settings']['filter-4-operator'].options] == [
|
||||
'eq',
|
||||
'ne',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
|
||||
resp.forms['listing-settings']['filter-4-value'].value = 'a@localhost'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
|
@ -959,6 +988,11 @@ def test_backoffice_date_filter(pub):
|
|||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'between',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
|
||||
resp.forms['listing-settings']['filter-4-value'].value = '2020-04-24'
|
||||
|
@ -1305,6 +1339,11 @@ def test_backoffice_table_varname_filter(pub):
|
|||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'between',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
|
@ -1418,6 +1457,11 @@ def test_backoffice_block_field_filter(pub):
|
|||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'between',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
resp.forms['listing-settings']['filter-0-1-value'].value = 'plop0'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
|
@ -1446,6 +1490,11 @@ def test_backoffice_block_field_filter(pub):
|
|||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'between',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
resp.forms['listing-settings']['filter-0-2-value'].value = '1'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
|
@ -1468,7 +1517,12 @@ def test_backoffice_block_field_filter(pub):
|
|||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.forms['listing-settings']['filter-0-3-value'].value == ''
|
||||
assert resp.forms['listing-settings']['filter-0-3-operator'].value == 'eq'
|
||||
assert [x[0] for x in resp.forms['listing-settings']['filter-0-3-operator'].options] == ['eq', 'ne']
|
||||
assert [x[0] for x in resp.forms['listing-settings']['filter-0-3-operator'].options] == [
|
||||
'eq',
|
||||
'ne',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
resp.forms['listing-settings']['filter-0-3-value'].value = 'true'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('<tr') == 1 + 6
|
||||
|
@ -1493,6 +1547,11 @@ def test_backoffice_block_field_filter(pub):
|
|||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'between',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
resp.forms['listing-settings']['filter-0-4-value'].value = '2021-06-01'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
|
@ -1514,7 +1573,14 @@ def test_backoffice_block_field_filter(pub):
|
|||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.forms['listing-settings']['filter-0-5-value'].value == ''
|
||||
assert resp.forms['listing-settings']['filter-0-5-operator'].value == 'eq'
|
||||
assert [x[0] for x in resp.forms['listing-settings']['filter-0-5-operator'].options] == ['eq', 'ne']
|
||||
assert [x[0] for x in resp.forms['listing-settings']['filter-0-5-operator'].options] == [
|
||||
'eq',
|
||||
'ne',
|
||||
'in',
|
||||
'not_in',
|
||||
'absent',
|
||||
'existing',
|
||||
]
|
||||
resp.forms['listing-settings']['filter-0-5-value'].value = 'a@localhost'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('<tr') == 1 + 6
|
||||
|
|
|
@ -1404,54 +1404,111 @@ def test_dynamic_item_field_from_custom_view_on_cards(pub, field_type):
|
|||
formdef.fields = [
|
||||
fields.PageField(id='2', label='1st page'),
|
||||
fields.ItemField(id='0', label='item', varname='blah', items=['foo', 'bar', 'baz']),
|
||||
fields.ItemField(id='3', label='item', varname='blah2', items=['foo', 'bar', 'baz']),
|
||||
fields.ItemField(id='1', label='string', data_source=ds, display_disabled_items=True),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
resp = get_app(pub).get('/test/')
|
||||
assert resp.form['f1'].options == [('', False, '---')]
|
||||
resp.form['f0'] = 'baz'
|
||||
live_resp = app.post('/test/live?modified_field_id=0', params=resp.form.submit_fields())
|
||||
assert len(live_resp.json['result']['1']['items']) == 10
|
||||
assert {str(x['id']) for x in live_resp.json['result']['1']['items']} == baz_ids
|
||||
def test(app):
|
||||
formdef.fields[3].display_mode = 'list'
|
||||
formdef.store()
|
||||
|
||||
resp.form['f1'].options = []
|
||||
for item in live_resp.json['result']['1']['items']:
|
||||
# simulate javascript filling the <select>
|
||||
resp.form['f1'].options.append((str(item['id']), False, item['text']))
|
||||
resp = get_app(pub).get('/test/')
|
||||
assert resp.form['f1'].options == [('', False, '---')]
|
||||
resp.form['f0'] = 'baz'
|
||||
resp.form['f3'] = 'foo'
|
||||
live_resp = app.post('/test/live?modified_field_id=0', params=resp.form.submit_fields())
|
||||
assert len(live_resp.json['result']['1']['items']) == 10
|
||||
assert {str(x['id']) for x in live_resp.json['result']['1']['items']} == baz_ids
|
||||
|
||||
resp.form['f1'] = resp.form['f1'].options[0][0]
|
||||
resp = resp.form.submit('submit') # -> validation page
|
||||
assert 'Technical error' not in resp.text
|
||||
resp = resp.form.submit('submit') # -> submit
|
||||
assert formdef.data_class().select()[0].data['1'] in baz_ids
|
||||
assert formdef.data_class().select()[0].data['1_structured']['item'] == 'baz'
|
||||
resp.form['f1'].options = []
|
||||
for item in live_resp.json['result']['1']['items']:
|
||||
# simulate javascript filling the <select>
|
||||
resp.form['f1'].options.append((str(item['id']), False, item['text']))
|
||||
|
||||
# same in autocomplete mode
|
||||
formdef.fields[2].display_mode = 'autocomplete'
|
||||
formdef.store()
|
||||
app = get_app(pub)
|
||||
resp = app.get('/test/')
|
||||
# simulate select2 mode, with qommon.forms.js adding an extra hidden widget
|
||||
resp.form.fields['f1_display'] = Hidden(form=resp.form, tag='input', name='f1_display', pos=10)
|
||||
select2_url = resp.pyquery('select:last').attr['data-select2-url']
|
||||
resp_json = app.get(select2_url + '?q=')
|
||||
assert len(resp_json.json['data']) == 0
|
||||
resp.form['f0'] = 'baz'
|
||||
resp.form['f1'] = resp.form['f1'].options[0][0]
|
||||
resp = resp.form.submit('submit') # -> validation page
|
||||
assert 'Technical error' not in resp.text
|
||||
resp = resp.form.submit('submit') # -> submit
|
||||
assert formdef.data_class().select()[0].data['1'] in baz_ids
|
||||
assert formdef.data_class().select()[0].data['1_structured']['item'] == 'baz'
|
||||
|
||||
live_resp = app.post('/test/live?modified_field_id=0', params=resp.form.submit_fields())
|
||||
new_select2_url = live_resp.json['result']['1']['source_url']
|
||||
resp_json = app.get(new_select2_url + '?q=')
|
||||
assert len(resp_json.json['data']) == 10
|
||||
assert {str(x['id']) for x in resp_json.json['data']} == baz_ids
|
||||
# same in autocomplete mode
|
||||
formdef.fields[3].display_mode = 'autocomplete'
|
||||
formdef.store()
|
||||
app = get_app(pub)
|
||||
resp = app.get('/test/')
|
||||
# simulate select2 mode, with qommon.forms.js adding an extra hidden widget
|
||||
resp.form.fields['f1_display'] = Hidden(form=resp.form, tag='input', name='f1_display', pos=10)
|
||||
select2_url = resp.pyquery('select:last').attr['data-select2-url']
|
||||
resp_json = app.get(select2_url + '?q=')
|
||||
assert len(resp_json.json['data']) == 0
|
||||
resp.form['f0'] = 'baz'
|
||||
resp.form['f3'] = 'foo'
|
||||
|
||||
resp.form['f1'].force_value(str(resp_json.json['data'][0]['id']))
|
||||
resp.form.fields['f1_display'].force_value(resp_json.json['data'][0]['text'])
|
||||
live_resp = app.post('/test/live?modified_field_id=0', params=resp.form.submit_fields())
|
||||
new_select2_url = live_resp.json['result']['1']['source_url']
|
||||
resp_json = app.get(new_select2_url + '?q=')
|
||||
assert len(resp_json.json['data']) == 10
|
||||
assert {str(x['id']) for x in resp_json.json['data']} == baz_ids
|
||||
|
||||
resp = resp.form.submit('submit') # -> validation page
|
||||
resp = resp.form.submit('submit') # -> submit
|
||||
assert formdef.data_class().select()[0].data['1'] in baz_ids
|
||||
assert formdef.data_class().select()[0].data['1_structured']['item'] == 'baz'
|
||||
resp.form['f1'].force_value(str(resp_json.json['data'][0]['id']))
|
||||
resp.form.fields['f1_display'].force_value(resp_json.json['data'][0]['text'])
|
||||
|
||||
resp = resp.form.submit('submit') # -> validation page
|
||||
resp = resp.form.submit('submit') # -> submit
|
||||
assert formdef.data_class().select()[0].data['1'] in baz_ids
|
||||
assert formdef.data_class().select()[0].data['1_structured']['item'] == 'baz'
|
||||
|
||||
test(app)
|
||||
|
||||
# operator with multi values - IN
|
||||
resp = app.get('/backoffice/data/items/%s/' % custom_view.slug)
|
||||
resp.forms['listing-settings']['filter-0-operator'] = 'in'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
if field_type == 'item':
|
||||
# some javascript to get a text input for filter value
|
||||
assert resp.forms['listing-settings']['filter-0-value'].attrs['data-allow-template']
|
||||
assert 'custom value' in [x[2] for x in resp.forms['listing-settings']['filter-0-value'].options]
|
||||
resp.forms['listing-settings']['filter-0-value'].force_value(
|
||||
'{{ form_var_blah }}|{{ form_var_blah }}'
|
||||
)
|
||||
else:
|
||||
resp.forms['listing-settings']['filter-0-value'] = '{{ form_var_blah }}|{{ form_var_blah }}'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.forms['listing-settings']['filter-0-value'].value == '{{ form_var_blah }}|{{ form_var_blah }}'
|
||||
assert resp.text.count('<tr') == 1 # thead only
|
||||
|
||||
# save custom view with filter
|
||||
resp = resp.forms['save-custom-view'].submit().follow()
|
||||
|
||||
test(app)
|
||||
|
||||
if field_type != 'email':
|
||||
# operator with multi values - BETWEEN
|
||||
resp = app.get('/backoffice/data/items/%s/' % custom_view.slug)
|
||||
resp.forms['listing-settings']['filter-0-operator'] = 'between'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
if field_type == 'item':
|
||||
# some javascript to get a text input for filter value
|
||||
assert resp.forms['listing-settings']['filter-0-value'].attrs['data-allow-template']
|
||||
assert 'custom value' in [x[2] for x in resp.forms['listing-settings']['filter-0-value'].options]
|
||||
resp.forms['listing-settings']['filter-0-value'].force_value(
|
||||
'{{ form_var_blah }}|{{ form_var_blah2 }}'
|
||||
)
|
||||
else:
|
||||
resp.forms['listing-settings']['filter-0-value'] = '{{ form_var_blah }}|{{ form_var_blah2 }}'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert (
|
||||
resp.forms['listing-settings']['filter-0-value'].value
|
||||
== '{{ form_var_blah }}|{{ form_var_blah2 }}'
|
||||
)
|
||||
assert resp.text.count('<tr') == 1 # thead only
|
||||
|
||||
# save custom view with filter
|
||||
resp = resp.forms['save-custom-view'].submit().follow()
|
||||
|
||||
test(app)
|
||||
|
||||
# delete custom view
|
||||
pub.loggederror_class.wipe()
|
||||
|
|
|
@ -51,10 +51,8 @@ from wcs.sql_criterias import (
|
|||
ElementIntersects,
|
||||
Equal,
|
||||
FtsMatch,
|
||||
Greater,
|
||||
GreaterOrEqual,
|
||||
Intersects,
|
||||
Less,
|
||||
LessOrEqual,
|
||||
Not,
|
||||
NotContains,
|
||||
|
@ -65,10 +63,10 @@ from wcs.sql_criterias import (
|
|||
StrictNotEqual,
|
||||
get_field_id,
|
||||
)
|
||||
from wcs.variables import LazyFieldVar, LazyList, NoneFieldVar
|
||||
from wcs.variables import LazyFieldVar, LazyFormDefObjectsManager, LazyList, NoneFieldVar
|
||||
from wcs.workflows import WorkflowStatusItem, item_classes, template_on_formdata
|
||||
|
||||
from ..qommon import _, audit, errors, ezt, force_str, get_cfg, misc, ngettext, ods, pgettext_lazy, template
|
||||
from ..qommon import _, audit, errors, ezt, get_cfg, misc, ngettext, ods, pgettext_lazy, template
|
||||
from ..qommon.afterjobs import AfterJob
|
||||
from ..qommon.evalutils import make_datetime
|
||||
from ..qommon.form import (
|
||||
|
@ -939,10 +937,16 @@ class FormPage(FormdefDirectoryBase):
|
|||
criterias=None,
|
||||
anonymised=False,
|
||||
):
|
||||
criterias = (criterias or [])[:]
|
||||
# remove potential filter on self
|
||||
filter_field_id = get_field_id(filter_field)
|
||||
criterias = [x for x in criterias if x.attribute != filter_field_id]
|
||||
filtered_criterias = []
|
||||
for criteria in criterias or []:
|
||||
if getattr(criteria, 'attribute', None) == filter_field_id:
|
||||
continue
|
||||
if isinstance(criteria, Not) and getattr(criteria.criteria, 'attribute', None) == filter_field_id:
|
||||
continue
|
||||
filtered_criterias.append(criteria)
|
||||
criterias = filtered_criterias
|
||||
# apply other filters
|
||||
if not anonymised:
|
||||
criterias.append(Null('anonymised'))
|
||||
|
@ -1183,7 +1187,9 @@ class FormPage(FormdefDirectoryBase):
|
|||
|
||||
filter_field_operator_key = '%s-operator' % filter_field_key.replace('-value', '')
|
||||
filter_field_operator = filters_dict.get(filter_field_operator_key) or 'eq'
|
||||
operators = self.get_field_allowed_operators(filter_field)
|
||||
|
||||
lazy_manager = LazyFormDefObjectsManager(formdef=self.formdef)
|
||||
operators = lazy_manager.get_field_allowed_operators(filter_field) or []
|
||||
|
||||
if filter_field.key == 'status':
|
||||
operators = [
|
||||
|
@ -1301,6 +1307,7 @@ class FormPage(FormdefDirectoryBase):
|
|||
if filter_field.key == 'item' and filter_field.get_display_mode() == 'autocomplete':
|
||||
display_mode = 'select2'
|
||||
|
||||
is_multi_values = filter_field_operator in ['in', 'not_in', 'between']
|
||||
if display_mode == 'select':
|
||||
options = self.get_item_filter_options(
|
||||
filter_field,
|
||||
|
@ -1312,7 +1319,9 @@ class FormPage(FormdefDirectoryBase):
|
|||
options.insert(0, (None, '', ''))
|
||||
attrs = {'data-refresh-options': str(filter_field.contextual_id)}
|
||||
else:
|
||||
options = [(filter_field_value, filter_field_value or '', filter_field_value or '')]
|
||||
options = [(None, '', '')]
|
||||
if not is_multi_values:
|
||||
options = [(filter_field_value, filter_field_value or '', filter_field_value or '')]
|
||||
attrs = {'data-remote-options': str(filter_field.contextual_id)}
|
||||
get_response().add_javascript(
|
||||
['jquery.js', '../../i18n.js', 'qommon.forms.js', 'select2.js']
|
||||
|
@ -1323,6 +1332,8 @@ class FormPage(FormdefDirectoryBase):
|
|||
if filter_field_value and filter_field_value not in [x[0] for x in options]:
|
||||
options.append((filter_field_value, filter_field_value, filter_field_value))
|
||||
attrs['data-allow-template'] = 'true'
|
||||
if is_multi_values:
|
||||
attrs['data-multi-values'] = filter_field_value
|
||||
|
||||
widget = SingleSelectWidget(
|
||||
filter_field_key,
|
||||
|
@ -1756,39 +1767,6 @@ class FormPage(FormdefDirectoryBase):
|
|||
query_overrides = get_request().form
|
||||
return self.get_view_criterias(query_overrides, statistics_fields_only=statistics_fields_only)
|
||||
|
||||
def get_field_allowed_operators(self, field):
|
||||
operators = [
|
||||
('eq', '='),
|
||||
('ne', '!='),
|
||||
]
|
||||
full_operators = operators + [
|
||||
('lt', '<'),
|
||||
('lte', '<='),
|
||||
('gt', '>'),
|
||||
('gte', '>='),
|
||||
]
|
||||
if field.key in ['internal-id', 'date', 'item', 'items', 'string']:
|
||||
return full_operators
|
||||
if field.key in ['bool', 'email']:
|
||||
return operators
|
||||
return None
|
||||
|
||||
def get_field_criteria(self, field, operator, field_key):
|
||||
mapping = {
|
||||
'eq': Equal,
|
||||
'ne': NotEqual,
|
||||
'lt': Less,
|
||||
'lte': LessOrEqual,
|
||||
'gt': Greater,
|
||||
'gte': GreaterOrEqual,
|
||||
}
|
||||
operators = self.get_field_allowed_operators(field)
|
||||
if operators is None:
|
||||
return
|
||||
if operator not in [o[0] for o in operators]:
|
||||
raise RequestError('Invalid operator "%s" for "%s"' % (operator, field_key))
|
||||
return mapping[operator]
|
||||
|
||||
def get_view_criterias(
|
||||
self,
|
||||
query_overrides=None,
|
||||
|
@ -1939,10 +1917,17 @@ class FormPage(FormdefDirectoryBase):
|
|||
# get operator and criteria
|
||||
filter_field_operator_key = '%s-operator' % filter_field_key.replace('-value', '')
|
||||
filter_field_operator = filters_dict.get(filter_field_operator_key) or 'eq'
|
||||
criteria = self.get_field_criteria(filter_field, filter_field_operator, filter_field_key)
|
||||
report_error_type = 'request-error' if self.view_type == 'json' else 'session-error'
|
||||
lazy_manager = LazyFormDefObjectsManager(
|
||||
formdef=self.formdef, report_error_type=report_error_type
|
||||
)
|
||||
|
||||
# check value types
|
||||
if filter_field.key == 'internal-id':
|
||||
if filter_field_operator not in ['eq', 'ne', 'lt', 'lte', 'gt', 'gte']:
|
||||
raise RequestError(
|
||||
'Invalid operator "%s" for "filter-internal-id"' % (filter_field_operator)
|
||||
)
|
||||
|
||||
def _report_error(value, operator):
|
||||
if custom_view:
|
||||
|
@ -1965,7 +1950,8 @@ class FormPage(FormdefDirectoryBase):
|
|||
|
||||
if Template.is_template_string(filter_field_value, ezt_support=False):
|
||||
if keep_templates:
|
||||
criterias.append(criteria('id', filter_field_value))
|
||||
# use Equal criteria here, the only use is in CardDef.get_data_source_referenced_varnames
|
||||
criterias.append(Equal('id', filter_field_value))
|
||||
continue
|
||||
if not compile_templates:
|
||||
criterias.append(Nothing())
|
||||
|
@ -2002,11 +1988,8 @@ class FormPage(FormdefDirectoryBase):
|
|||
filter_date_value = misc.get_as_datetime(filter_field_value).timetuple()
|
||||
except ValueError:
|
||||
continue
|
||||
elif filter_field.key == 'date':
|
||||
try:
|
||||
filter_field_value = misc.get_as_datetime(filter_field_value).date().strftime('%Y-%m-%d')
|
||||
except ValueError:
|
||||
continue
|
||||
elif filter_field_value == 'on' and filter_field_operator in ['absent', 'existing']:
|
||||
filter_field_value = Ellipsis
|
||||
elif filter_field.key == 'bool':
|
||||
if filter_field_value == 'true':
|
||||
filter_field_value = True
|
||||
|
@ -2017,24 +2000,36 @@ class FormPage(FormdefDirectoryBase):
|
|||
elif filter_field.key in ('item', 'items', 'string', 'email'):
|
||||
if Template.is_template_string(filter_field_value, ezt_support=False):
|
||||
if keep_templates:
|
||||
criterias.append(criteria(filter_field.id, filter_field_value))
|
||||
# use Equal criteria here, the only use is in CardDef.get_data_source_referenced_varnames
|
||||
criterias.append(Equal(filter_field.id, filter_field_value))
|
||||
continue
|
||||
if not compile_templates:
|
||||
criterias.append(Nothing())
|
||||
continue
|
||||
filter_field_value = WorkflowStatusItem.compute(filter_field_value)
|
||||
|
||||
if filter_field.key in ('item', 'items', 'bool', 'string', 'email', 'date'):
|
||||
operators = lazy_manager.get_field_allowed_operators(filter_field) or []
|
||||
if filter_field_operator not in [o[0] for o in operators]:
|
||||
raise RequestError(
|
||||
'Invalid operator "%s" for "%s"' % (filter_field_operator, filter_field_key)
|
||||
)
|
||||
try:
|
||||
# cast to integer so it can be used with numerical operators
|
||||
# (limit to 32bits to match postgresql integer range)
|
||||
int_filter_field_value = int(filter_field_value)
|
||||
if -(2**31) <= int_filter_field_value < 2**31:
|
||||
filter_field_value = int_filter_field_value
|
||||
except (ValueError, TypeError):
|
||||
filter_field_value = str(filter_field_value)
|
||||
filter_field_value = lazy_manager.format_value(
|
||||
op=filter_field_operator,
|
||||
value=filter_field_value,
|
||||
field=filter_field,
|
||||
)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# add criteria
|
||||
if filter_field.key == 'internal-id':
|
||||
criterias.append(criteria('id', filter_field_value))
|
||||
criterias.append(
|
||||
lazy_manager.get_criteria_from_operator(
|
||||
op=filter_field_operator, value=filter_field_value, field_id='id'
|
||||
)
|
||||
)
|
||||
elif filter_field.key == 'number':
|
||||
criterias.append(Equal('id_display', str(filter_field_value)))
|
||||
elif filter_field.key == 'period-date':
|
||||
|
@ -2088,7 +2083,14 @@ class FormPage(FormdefDirectoryBase):
|
|||
center = misc.normalize_geolocation({'lat': center_lat, 'lon': center_lon})
|
||||
criterias.append(Distance(center, float(filter_field_value)))
|
||||
elif filter_field.key in ('item', 'items', 'bool', 'string', 'email', 'date'):
|
||||
criterias.append(criteria('f%s' % filter_field.id, filter_field_value, field=filter_field))
|
||||
criterias.append(
|
||||
lazy_manager.get_criteria_from_operator(
|
||||
op=filter_field_operator,
|
||||
value=filter_field_value,
|
||||
field_id='f%s' % filter_field.id,
|
||||
field=filter_field,
|
||||
)
|
||||
)
|
||||
if filter_field.key in ('item', 'items'):
|
||||
criterias[-1]._label = '%s: %s' % (
|
||||
filter_field.label,
|
||||
|
|
|
@ -2511,7 +2511,7 @@ del {
|
|||
border-bottom: 1px solid #AAA;
|
||||
appearance: auto;
|
||||
padding-right: 0;
|
||||
width: 4em;
|
||||
width: 6em;
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -626,3 +626,14 @@ div.file-button .widget-message {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.multi-value-filter {
|
||||
&--add-value-button,
|
||||
&--remove-value-button {
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
padding: 0;
|
||||
margin: auto 0 auto 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,6 +152,40 @@ function refresh_table() {
|
|||
{qs: $('form#listing-settings').serialize(), auto: false});
|
||||
}
|
||||
|
||||
function prepare_select2($elem) {
|
||||
var filter_field_id = $elem.data('remote-options');
|
||||
var options = {
|
||||
language: {
|
||||
errorLoading: function() { return WCS_I18N.s2_errorloading; },
|
||||
noResults: function () { return WCS_I18N.s2_nomatches; },
|
||||
inputTooShort: function (input, min) { return WCS_I18N.s2_tooshort; },
|
||||
loadingMore: function () { return WCS_I18N.s2_loadmore; },
|
||||
searching: function () { return WCS_I18N.s2_searching; },
|
||||
},
|
||||
placeholder: '',
|
||||
allowClear: true,
|
||||
minimumInputLength: 1,
|
||||
ajax: {
|
||||
url: function() {
|
||||
var pathname = window.location.pathname.replace(/^\/+/, '/').replace(/stats$/, '');
|
||||
var filter_settings = $('form#listing-settings').serialize();
|
||||
return pathname + 'filter-options?filter_field_id=' + filter_field_id + '&' + filter_settings;
|
||||
},
|
||||
dataType: 'json',
|
||||
data: function(params) {
|
||||
var query = {
|
||||
_search: params.term,
|
||||
}
|
||||
return query;
|
||||
},
|
||||
processResults: function (data, params) {
|
||||
return {results: data.data};
|
||||
},
|
||||
},
|
||||
};
|
||||
$elem.select2(options);
|
||||
}
|
||||
|
||||
$(document).on('backoffice-filter-change', function(event, listing_settings) {
|
||||
/* makes sure it doesn't start with a double slash */
|
||||
var pathname = window.location.pathname.replace(/^\/+/, '/');
|
||||
|
@ -226,7 +260,13 @@ $(document).on('backoffice-filter-change', function(event, listing_settings) {
|
|||
var $option = $('<option></option>', {value: current_value, text: current_value});
|
||||
$option.attr('selected', 'selected');
|
||||
$option.appendTo($select);
|
||||
} else if (current_value && !found_current_value && $select.is('[data-multi-values]')) {
|
||||
var $option = $('<option data-option-for-multi-values>multi</option>');
|
||||
$option.attr('value', current_value);
|
||||
$option.attr('selected', 'selected');
|
||||
$option.appendTo($select);
|
||||
}
|
||||
$('.operator select').trigger('change', true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -309,37 +349,7 @@ $(function() {
|
|||
|
||||
/* set filter options from server (select2) */
|
||||
$('[data-remote-options]').each(function(idx, elem) {
|
||||
var filter_field_id = $(elem).data('remote-options');
|
||||
var options = {
|
||||
language: {
|
||||
errorLoading: function() { return WCS_I18N.s2_errorloading; },
|
||||
noResults: function () { return WCS_I18N.s2_nomatches; },
|
||||
inputTooShort: function (input, min) { return WCS_I18N.s2_tooshort; },
|
||||
loadingMore: function () { return WCS_I18N.s2_loadmore; },
|
||||
searching: function () { return WCS_I18N.s2_searching; },
|
||||
},
|
||||
placeholder: '',
|
||||
allowClear: true,
|
||||
minimumInputLength: 1,
|
||||
ajax: {
|
||||
url: function() {
|
||||
var pathname = window.location.pathname.replace(/^\/+/, '/').replace(/stats$/, '');
|
||||
var filter_settings = $('form#listing-settings').serialize();
|
||||
return pathname + 'filter-options?filter_field_id=' + filter_field_id + '&' + filter_settings;
|
||||
},
|
||||
dataType: 'json',
|
||||
data: function(params) {
|
||||
var query = {
|
||||
_search: params.term,
|
||||
}
|
||||
return query;
|
||||
},
|
||||
processResults: function (data, params) {
|
||||
return {results: data.data};
|
||||
},
|
||||
},
|
||||
};
|
||||
$(elem).select2(options);
|
||||
prepare_select2($(elem));
|
||||
});
|
||||
|
||||
$('button#save-view').on('click', function() {
|
||||
|
@ -372,7 +382,7 @@ $(function() {
|
|||
});
|
||||
|
||||
/* automatically refresh onfilter change */
|
||||
$('form#listing-settings input[type=date], form#listing-settings input[type=text], form#listing-settings select').change(function() {
|
||||
$(document).on('change', 'form#listing-settings input[type=date], form#listing-settings input[type=text], form#listing-settings .value select', function () {
|
||||
if (this.type == 'date' && $(this).val() && $(this).val()[0] == '0') {
|
||||
// input date with year starting with 0, it's currently being typed in,
|
||||
// wait for the year to be complete before acting on it.
|
||||
|
@ -400,6 +410,197 @@ $(function() {
|
|||
return false;
|
||||
});
|
||||
|
||||
/* operator selection */
|
||||
$('.operator select').change(function (event, init) {
|
||||
var operator = $(this).val();
|
||||
var $container = $(this).parents('.operator-and-value-widget');
|
||||
var $value_container = $container.find('.value:not([data-for-multi-values])');
|
||||
var $value_input = $value_container.find('input');
|
||||
var $value_select = $value_container.find('select');
|
||||
|
||||
$value_container.show();
|
||||
// remove added 'on' value of select options
|
||||
$value_select.find('option[data-on-option]').remove();
|
||||
// remove inputs and selects for multi values
|
||||
$container.find('.value[data-for-multi-values]').each(function() {
|
||||
$(this).remove();
|
||||
if ($(this).data('select2-id')) {
|
||||
$(this).select2('destroy');
|
||||
}
|
||||
});
|
||||
// remove added 'multi' value of select options
|
||||
$value_select.find('option[data-option-for-multi-values]').remove();
|
||||
|
||||
if (operator == 'absent' || operator == 'existing') {
|
||||
// absent or existing filter, no input/select to set a value, but 'on' value is needed to apply filter
|
||||
$value_input.val('on');
|
||||
if ($value_select.length) {
|
||||
// add an 'on' value for absent/existing operators
|
||||
$('<option value="on" data-on-option>on</option>').appendTo($value_select);
|
||||
}
|
||||
$value_select.val('on');
|
||||
$value_container.hide();
|
||||
} else {
|
||||
if ($value_input.val() == 'on') {
|
||||
$value_input.val('');
|
||||
}
|
||||
if ($value_select.val() == 'on') {
|
||||
$value_select.val('');
|
||||
}
|
||||
}
|
||||
|
||||
if (operator == 'between' || operator == 'in' || operator == 'not_in') {
|
||||
// operator with multi values
|
||||
// hide original value input or select
|
||||
$value_container.hide();
|
||||
// multi input/select needed
|
||||
var $value_element = null;
|
||||
if ($value_select.length) {
|
||||
$value_element = $value_select;
|
||||
} else {
|
||||
$value_element = $value_input;
|
||||
}
|
||||
var values = [];
|
||||
if ($value_element.attr('data-multi-values')) {
|
||||
values = $value_element.attr('data-multi-values').split('|');
|
||||
} else if ($value_element.val()) {
|
||||
if ($value_element.val().indexOf('|') == -1) {
|
||||
values = [$value_element.val()];
|
||||
} else {
|
||||
values = $value_element.val().split('|')
|
||||
}
|
||||
}
|
||||
for (let value of values) {
|
||||
var $copy = $value_container.clone().attr('data-for-multi-values', '').show();
|
||||
$copy.find($value_element.prop('tagName'))
|
||||
.removeAttr('id')
|
||||
.removeAttr('data-refresh-options')
|
||||
.removeAttr('data-multi-values')
|
||||
.attr('name', 'multi-' + $value_element.attr('name'))
|
||||
.val(value);
|
||||
|
||||
if($value_input.length) {
|
||||
$remove_button = $('<button class="multi-value-filter--remove-value-button">-</button>')
|
||||
$remove_button.click(function() {
|
||||
values = values.filter(item => item !== value)
|
||||
if($value_select.length) {
|
||||
$value_select
|
||||
.find('option[data-option-for-multi-values]')
|
||||
.attr('value', values.join('|'));
|
||||
}
|
||||
else {
|
||||
$value_input.val(values.join('|'))
|
||||
}
|
||||
|
||||
$('form#listing-settings').submit();
|
||||
});
|
||||
$copy.append($remove_button);
|
||||
}
|
||||
|
||||
$container.append($copy);
|
||||
if ($value_select.data('select2-id')) {
|
||||
$copy.find('span.select2').remove();
|
||||
if (! $copy.find('option[value="' + value + '"]').length) {
|
||||
var $option = $('<option></option>', {value: value, text: value});
|
||||
$option.attr('selected', 'selected');
|
||||
$option.appendTo($copy.find('select'));
|
||||
}
|
||||
$select = $copy.find('select')
|
||||
prepare_select2($select);
|
||||
$select.on('select2:opening', (e) => {
|
||||
e.preventDefault()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var $add_item = $value_container.clone().attr('data-for-multi-values', '').show();
|
||||
$add_item.find($value_element.prop('tagName'))
|
||||
.removeAttr('id')
|
||||
.removeAttr('data-refresh-options')
|
||||
.removeAttr('data-multi-values')
|
||||
.attr('name', 'multi-' + $value_element.attr('name'))
|
||||
.val('');
|
||||
|
||||
if($value_input.length) {
|
||||
$add_button = $('<button class="multi-value-filter--add-value-button">+</button>')
|
||||
$add_item.append($add_button)
|
||||
$add_button.click(function() {
|
||||
$('form#listing-settings').submit();
|
||||
});
|
||||
}
|
||||
|
||||
if ($value_select.data('select2-id')) {
|
||||
$add_item.find('span.select2').remove();
|
||||
prepare_select2($add_item.find('select'));
|
||||
}
|
||||
|
||||
$container.append($add_item)
|
||||
|
||||
if ($value_select.length && values.length) {
|
||||
// add an option for 'multi' value
|
||||
var $option = $('<option data-option-for-multi-values>multi</option>');
|
||||
$option.attr('value', values.join('|'));
|
||||
$option.attr('selected', 'selected');
|
||||
$option.appendTo($value_select);
|
||||
$value_select.attr('data-multi-values', values.join('|'));
|
||||
} else if ($value_input.length && values.length) {
|
||||
$value_input.val(values.join('|'));
|
||||
}
|
||||
} else {
|
||||
// remove multi values
|
||||
if ($value_input.length && $value_input.val()) {
|
||||
var values = $value_input.val().split('|');
|
||||
$value_input.val(values[0]);
|
||||
}
|
||||
if ($value_select.length && $value_select.attr('data-multi-values')) {
|
||||
var values = $value_select.attr('data-multi-values').split('|');
|
||||
$value_select.val(values[0]);
|
||||
$value_select.removeAttr('data-multi-values')
|
||||
if ($value_select.data('select2-id') && ! $value_select.find('option[value="' + values[0] + '"]').length) {
|
||||
var $option = $('<option></option>', {value: values[0], text: values[0]});
|
||||
$option.attr('selected', 'selected');
|
||||
$option.appendTo($value_select);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!init) {
|
||||
$('form#listing-settings').submit();
|
||||
}
|
||||
});
|
||||
$('.operator select').trigger('change', true);
|
||||
|
||||
$(document).on('change', '.value[data-for-multi-values] input, .value[data-for-multi-values] select', function () {
|
||||
var $container = $(this).parents('.operator-and-value-widget');
|
||||
var $operator = $container.find('.operator select');
|
||||
var $value_input = $container.find('.value:not([data-for-multi-values]) input');
|
||||
var $value_select = $container.find('.value:not([data-for-multi-values]) select');
|
||||
var values = [];
|
||||
$container.find('.value[data-for-multi-values] input, .value[data-for-multi-values] select').each(function () {
|
||||
if ($(this).val()) {
|
||||
values.push($(this).val());
|
||||
}
|
||||
});
|
||||
if ($operator.val() == 'between' && values.length != 2) {
|
||||
// wrong number of values, don't call backoffice
|
||||
return;
|
||||
}
|
||||
if ($value_input.length) {
|
||||
// set multi value
|
||||
$value_input.val(values.join('|'));
|
||||
} else {
|
||||
// add a 'multi' value
|
||||
var $option = $('<option data-option-for-multi-values>multi</option>');
|
||||
$option.attr('value', values.join('|'));
|
||||
$option.attr('selected', 'selected');
|
||||
$option.appendTo($value_select);
|
||||
// and update data-multi-values
|
||||
$value_select.attr('data-multi-values', values.join('|'));
|
||||
}
|
||||
// submit form
|
||||
$('form#listing-settings').submit();
|
||||
});
|
||||
|
||||
/* refresh every 30 seconds (idle_id) after any user activity
|
||||
* on inactivity for more than 5 minutes (longidle_id), stop refreshing (clear idle_id)
|
||||
*/
|
||||
|
|
|
@ -170,7 +170,7 @@ class Criteria:
|
|||
def __repr__(self):
|
||||
return '<%s (attribute: %r%s)>' % (
|
||||
self.__class__.__name__,
|
||||
self.attribute,
|
||||
getattr(self, 'attribute', None),
|
||||
', value: %r' % self.value if hasattr(self, 'value') else '',
|
||||
)
|
||||
|
||||
|
|
123
wcs/variables.py
123
wcs/variables.py
|
@ -19,7 +19,8 @@ import warnings
|
|||
from django.utils import formats
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from quixote import get_publisher, get_request
|
||||
from quixote import get_publisher, get_request, get_session
|
||||
from quixote.errors import RequestError
|
||||
|
||||
from wcs.sql_criterias import (
|
||||
And,
|
||||
|
@ -51,7 +52,14 @@ from .qommon.templatetags.qommon import parse_datetime
|
|||
|
||||
class LazyFormDefObjectsManager:
|
||||
def __init__(
|
||||
self, formdef, formdata=None, geoloc_center_formdata=None, criterias=None, order_by=None, limit=None
|
||||
self,
|
||||
formdef,
|
||||
formdata=None,
|
||||
geoloc_center_formdata=None,
|
||||
criterias=None,
|
||||
order_by=None,
|
||||
limit=None,
|
||||
report_error_type=None,
|
||||
):
|
||||
self._formdef = formdef
|
||||
self._formdata = formdata
|
||||
|
@ -69,6 +77,18 @@ class LazyFormDefObjectsManager:
|
|||
self._order_by = order_by
|
||||
self._limit = limit
|
||||
self._cached_resultset = None
|
||||
self._report_error_type = report_error_type or 'record-error'
|
||||
|
||||
def report_error(self, error_message):
|
||||
if self._report_error_type == 'request-error':
|
||||
raise RequestError(error_message)
|
||||
if self._report_error_type == 'session-error':
|
||||
get_session().message = ('warning', error_message)
|
||||
if self._report_error_type == 'record-error':
|
||||
get_publisher().record_error(
|
||||
error_message,
|
||||
formdata=self._formdata,
|
||||
)
|
||||
|
||||
@property # @property for backward compatibility
|
||||
def count(self):
|
||||
|
@ -88,9 +108,8 @@ class LazyFormDefObjectsManager:
|
|||
|
||||
def order_by(self, attribute):
|
||||
if not isinstance(attribute, str):
|
||||
get_publisher().record_error(
|
||||
self.report_error(
|
||||
_('Invalid value %r for "order_by"') % attribute,
|
||||
formdata=self._formdata,
|
||||
)
|
||||
return self.none()
|
||||
field = self.get_field(attribute)
|
||||
|
@ -119,10 +138,9 @@ class LazyFormDefObjectsManager:
|
|||
|
||||
def filter_by_user(self, user, op='eq'):
|
||||
if op not in ['eq']:
|
||||
get_publisher().record_error(
|
||||
_('Invalid operator "%(op)s" for filter "%(filter)s"')
|
||||
% {'op': self.get_operator_name(op), 'filter': self.pending_attr},
|
||||
formdata=self._formdata,
|
||||
self.report_error(
|
||||
_('Invalid operator "%(operator)s" for filter "%(filter)s"')
|
||||
% {'operator': self.get_operator_name(op), 'filter': self.pending_attr},
|
||||
)
|
||||
return self.none()
|
||||
if isinstance(user, str):
|
||||
|
@ -133,10 +151,9 @@ class LazyFormDefObjectsManager:
|
|||
|
||||
def filter_by_status(self, status, op='eq'):
|
||||
if op not in ['eq', 'ne']:
|
||||
get_publisher().record_error(
|
||||
_('Invalid operator "%(op)s" for filter "%(filter)s"')
|
||||
% {'op': self.get_operator_name(op), 'filter': self.pending_attr},
|
||||
formdata=self._formdata,
|
||||
self.report_error(
|
||||
_('Invalid operator "%(operator)s" for filter "%(filter)s"')
|
||||
% {'operator': self.get_operator_name(op), 'filter': self.pending_attr},
|
||||
)
|
||||
return self.none()
|
||||
for wfs in self._formdef.workflow.possible_status:
|
||||
|
@ -205,19 +222,21 @@ class LazyFormDefObjectsManager:
|
|||
return qs
|
||||
|
||||
def filter_by_internal_id(self, value, op='eq'):
|
||||
if op not in ['eq', 'ne', 'lt', 'lte', 'gt', 'gte']:
|
||||
get_publisher().record_error(
|
||||
_('Invalid operator "%(op)s" for filter "%(filter)s"')
|
||||
% {'op': self.get_operator_name(op), 'filter': self.pending_attr},
|
||||
formdata=self._formdata,
|
||||
from wcs.backoffice.management import FakeField
|
||||
|
||||
field = FakeField('internal-id', 'internal-id', 'fake')
|
||||
operators = self.get_field_allowed_operators(field)
|
||||
if op not in [o[0] for o in operators]:
|
||||
self.report_error(
|
||||
_('Invalid operator "%(operator)s" for filter "%(filter)s"')
|
||||
% {'operator': self.get_operator_name(op), 'filter': self.pending_attr},
|
||||
)
|
||||
return self.none()
|
||||
try:
|
||||
int(value)
|
||||
except (ValueError, TypeError):
|
||||
get_publisher().record_error(
|
||||
self.report_error(
|
||||
_('Invalid value "%s" for filter "internal_id"') % (value),
|
||||
formdata=self._formdata,
|
||||
)
|
||||
return self.none()
|
||||
return self._clone(
|
||||
|
@ -226,10 +245,9 @@ class LazyFormDefObjectsManager:
|
|||
|
||||
def filter_by_number(self, value, op='eq'):
|
||||
if op not in ['eq']:
|
||||
get_publisher().record_error(
|
||||
self.report_error(
|
||||
_('Invalid operator "%(op)s" for filter "%(filter)s"')
|
||||
% {'op': self.get_operator_name(op), 'filter': self.pending_attr},
|
||||
formdata=self._formdata,
|
||||
)
|
||||
return self.none()
|
||||
return self._clone(self._criterias + [Equal('id_display', str(value))])
|
||||
|
@ -248,15 +266,41 @@ class LazyFormDefObjectsManager:
|
|||
return field
|
||||
|
||||
def get_field_allowed_operators(self, field):
|
||||
operators = ['eq', 'ne', 'absent', 'existing']
|
||||
more_operators = operators + ['in', 'not_in']
|
||||
full_operators = more_operators + ['lt', 'lte', 'gt', 'gte', 'between']
|
||||
equality_operators = [
|
||||
('eq', '='),
|
||||
('ne', '!='),
|
||||
]
|
||||
comparison_operators = [
|
||||
('lt', '<'),
|
||||
('lte', '<='),
|
||||
('gt', '>'),
|
||||
('gte', '>='),
|
||||
]
|
||||
more_comparison_operators = [
|
||||
('between', _('between')),
|
||||
]
|
||||
empty_operators = [
|
||||
('absent', _('absent')),
|
||||
('existing', _('existing')),
|
||||
]
|
||||
in_operators = [
|
||||
('in', _('in')),
|
||||
('not_in', _('not in')),
|
||||
]
|
||||
if field.key == 'internal-id':
|
||||
return equality_operators + comparison_operators
|
||||
if field.key in ['date', 'item', 'items', 'string']:
|
||||
return full_operators
|
||||
return (
|
||||
equality_operators
|
||||
+ comparison_operators
|
||||
+ more_comparison_operators
|
||||
+ in_operators
|
||||
+ empty_operators
|
||||
)
|
||||
if field.key == 'bool':
|
||||
return operators
|
||||
return equality_operators + empty_operators
|
||||
if field.key == 'email':
|
||||
return more_operators
|
||||
return equality_operators + in_operators + empty_operators
|
||||
return None
|
||||
|
||||
def format_value(self, op, value, field):
|
||||
|
@ -276,10 +320,9 @@ class LazyFormDefObjectsManager:
|
|||
try:
|
||||
value = field.convert_value_from_anything(value)
|
||||
except (ValueError, AttributeError):
|
||||
get_publisher().record_error(
|
||||
self.report_error(
|
||||
_('Invalid value "%(value)s" for filter "%(filter)s"')
|
||||
% {'value': value, 'filter': self.pending_attr},
|
||||
formdata=self._formdata,
|
||||
)
|
||||
raise ValueError
|
||||
if value is not None and hasattr(field, 'block_field') and hasattr(field, 'get_json_value'):
|
||||
|
@ -300,14 +343,18 @@ class LazyFormDefObjectsManager:
|
|||
convert_value(value=x.strip(), field=field) for x in str(value).split('|') if x.strip()
|
||||
]
|
||||
if op == 'between' and len(new_value) != 2:
|
||||
get_publisher().record_error(
|
||||
pending_attr = getattr(self, 'pending_attr', None)
|
||||
if not pending_attr:
|
||||
pending_attr = field.varname or field.id
|
||||
if self._report_error_type == 'request-error':
|
||||
pending_attr = 'filter-%s' % pending_attr
|
||||
self.report_error(
|
||||
_('Invalid value "%(value)s" for operator "%(operator)s" and filter "%(filter)s"')
|
||||
% {
|
||||
'value': value,
|
||||
'operator': self.get_operator_name(op),
|
||||
'filter': self.pending_attr,
|
||||
'filter': pending_attr,
|
||||
},
|
||||
formdata=self._formdata,
|
||||
)
|
||||
raise ValueError
|
||||
value = new_value
|
||||
|
@ -402,9 +449,7 @@ class LazyFormDefObjectsManager:
|
|||
|
||||
fields = list(self.get_fields(self.pending_attr))
|
||||
if not fields:
|
||||
get_publisher().record_error(
|
||||
_('Invalid filter "%s"') % self.pending_attr, formdata=self._formdata
|
||||
)
|
||||
self.report_error(_('Invalid filter "%s"') % self.pending_attr)
|
||||
return self.none()
|
||||
|
||||
# check operator
|
||||
|
@ -412,11 +457,10 @@ class LazyFormDefObjectsManager:
|
|||
if field.key not in ['date', 'item', 'items', 'string', 'bool', 'email']:
|
||||
continue
|
||||
operators = self.get_field_allowed_operators(field) or []
|
||||
if op not in operators:
|
||||
get_publisher().record_error(
|
||||
if op not in [o[0] for o in operators]:
|
||||
self.report_error(
|
||||
_('Invalid operator "%(operator)s" for filter "%(filter)s"')
|
||||
% {'operator': self.get_operator_name(op), 'filter': self.pending_attr},
|
||||
formdata=self._formdata,
|
||||
)
|
||||
return self.none()
|
||||
|
||||
|
@ -453,9 +497,8 @@ class LazyFormDefObjectsManager:
|
|||
|
||||
def apply_exclude_value(self, value):
|
||||
if hasattr(self, 'pending_op'):
|
||||
get_publisher().record_error(
|
||||
self.report_error(
|
||||
_('Operator filter is not allowed for exclude_value filter'),
|
||||
formdata=self._formdata,
|
||||
)
|
||||
return self.none()
|
||||
return self.apply_filter_value(value, exclude=True)
|
||||
|
|
Loading…
Reference in New Issue