Skip to content

Commit 972fde2

Browse files
authored
Merge pull request #247 from kobotoolbox/243-support-new-pyxform-features
Add support for pyxform v1.5.0
2 parents 661d642 + 4373aef commit 972fde2

File tree

7 files changed

+122
-45
lines changed

7 files changed

+122
-45
lines changed

src/formpack/schema/fields.py

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -160,23 +160,47 @@ def from_json_definition(cls, definition, hierarchy=None,
160160
choice = field_choices[choice_id]
161161

162162
data_type_classes = {
163-
"select_one": FormChoiceField,
164-
"select_multiple": FormChoiceFieldWithMultipleSelect,
165-
"geopoint": FormGPSField,
166-
"date": DateField,
167-
"text": TextField,
168-
"barcode": TextField,
169-
170-
# calculate is usually not text but for our purpose it's good
171-
# enough
172-
"calculate": TextField,
173-
"acknowledge": TextField,
174-
"integer": NumField,
163+
# selects
164+
'select_one': FormChoiceField,
165+
'select_one_from_file': FormChoiceField,
166+
'select_multiple': FormChoiceFieldWithMultipleSelect,
167+
# TODO: Get this to work with FormChoiceFieldWithMultipleSelect
168+
'select_multiple_from_file': TextField,
169+
'rank': TextField,
170+
171+
# date and time
172+
'date': DateField,
173+
'today': DateField,
174+
'datetime': TextField,
175+
'time': TextField,
176+
'start': TextField,
177+
'end': TextField,
178+
179+
# general
180+
'text': TextField,
181+
'barcode': TextField,
182+
'acknowledge': TextField,
183+
184+
# geo
185+
'geopoint': FormGPSField,
186+
'start-geopoint': FormGPSField,
187+
188+
# media
189+
'video': TextField,
190+
'image': TextField,
191+
'audio': TextField,
192+
'file': TextField,
193+
'background-audio': TextField,
194+
195+
# numeric
196+
'calculate': TextField,
197+
'integer': NumField,
175198
'decimal': NumField,
199+
'range': NumField,
176200

177201
# legacy type, treat them as text
178-
"select_one_external": partial(TextField, data_type=data_type),
179-
"cascading_select": partial(TextField, data_type=data_type),
202+
'select_one_external': partial(TextField, data_type=data_type),
203+
'cascading_select': partial(TextField, data_type=data_type),
180204
}
181205

182206
args = {

src/formpack/utils/expand_content.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from .array_to_xpath import EXPANDABLE_FIELD_TYPES
1212
from .future import iteritems, OrderedDict
1313
from .iterator import get_first_occurrence
14-
from .replace_aliases import META_TYPES
14+
from .replace_aliases import META_TYPES, SELECT_TYPES
1515
from .string import str_types
1616
from ..constants import (UNTRANSLATED, OR_OTHER_COLUMN,
1717
TAG_COLUMNS_AND_SEPARATORS)
@@ -231,6 +231,7 @@ def _mark_special(**kwargs):
231231

232232

233233
def _expand_type_to_dict(type_str):
234+
SELECT_PATTERN = r'^({select_type})\s+(\S+)$'
234235
out = {}
235236
match = re.search('( or.other)$', type_str)
236237
if match:
@@ -243,12 +244,10 @@ def _expand_type_to_dict(type_str):
243244
if type_str in ['select_one', 'select_multiple']:
244245
out['type'] = type_str
245246
return out
246-
for _re in [
247-
r'^(select_one)\s+(\S+)$',
248-
r'^(select_multiple)\s+(\S+)$',
249-
r'^(select_one_external)\s+(\S+)$',
250-
]:
251-
match = re.match(_re, type_str)
247+
for select_type in SELECT_TYPES:
248+
match = re.match(
249+
SELECT_PATTERN.format(select_type=select_type), type_str
250+
)
252251
if match:
253252
(type_, list_name) = match.groups()
254253
out['type'] = type_

src/formpack/utils/flatten_content.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .array_to_xpath import array_to_xpath
1111
from .future import range
1212
from .string import str_types
13+
from .replace_aliases import SELECT_TYPES
1314
from ..constants import (UNTRANSLATED, OR_OTHER_COLUMN,
1415
TAG_COLUMNS_AND_SEPARATORS)
1516

@@ -72,11 +73,10 @@ def _stringify_type__depr(json_qtype):
7273
{'select_one': 'xyz'} -> 'select_one xyz'
7374
{'select_multiple': 'xyz'} -> 'select_mutliple xyz'
7475
"""
75-
_type_keys = ['select_one', 'select_multiple']
7676
if len(json_qtype.keys()) != 1:
7777
raise ValueError('Type object must have exactly one key: %s' %
78-
', '.join(_type_keys))
79-
for try_key in _type_keys:
78+
', '.join(SELECT_TYPES))
79+
for try_key in SELECT_TYPES:
8080
if try_key in json_qtype:
8181
return '{} {}'.format(try_key, json_qtype[try_key])
8282
if 'select_one_or_other' in json_qtype:

src/formpack/utils/replace_aliases.py

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -66,25 +66,35 @@ def aliases_to_ordered_dict(_d):
6666
'geopoint': ['gps'],
6767
})
6868

69-
selects = aliases_to_ordered_dict({
70-
'select_multiple': [
71-
'select all that apply',
72-
'select multiple',
73-
'select many',
74-
'select_many',
75-
'select all that apply from',
76-
'add select multiple prompt using',
77-
],
78-
'select_one_external': [
79-
'select one external',
80-
],
81-
'select_one': [
82-
'select one',
83-
'select one from',
84-
'add select one prompt using',
85-
'select1',
86-
],
87-
})
69+
# keys used in `_expand_type_to_dict()` to handle choices argument
70+
selects = aliases_to_ordered_dict(
71+
{
72+
'select_multiple': [
73+
'select all that apply',
74+
'select multiple',
75+
'select many',
76+
'select_many',
77+
'select all that apply from',
78+
'add select multiple prompt using',
79+
],
80+
'select_multiple_from_file': [
81+
'select multiple from file',
82+
],
83+
'select_one_external': [
84+
'select one external',
85+
],
86+
'select_one': [
87+
'select one',
88+
'select one from',
89+
'add select one prompt using',
90+
'select1',
91+
],
92+
'select_one_from_file': [
93+
'select one from file',
94+
],
95+
'rank': [],
96+
}
97+
)
8898
# Python3: Cast to a list because it's merged into other dicts
8999
# (i.e `SELECT_SCHEMA` in validators.py)
90100
SELECT_TYPES = list(selects.keys())
@@ -96,12 +106,15 @@ def aliases_to_ordered_dict(_d):
96106
'deviceid',
97107
'phone_number',
98108
'simserial',
109+
'audit',
99110
# meta values
100111
'username',
101112
# reconsider:
102113
'phonenumber',
103114
'imei',
104115
'subscriberid',
116+
# geo
117+
'start-geopoint',
105118
]
106119

107120
LABEL_OPTIONAL_TYPES = [
@@ -128,17 +141,23 @@ def aliases_to_ordered_dict(_d):
128141
'video',
129142
'image',
130143
'audio',
144+
'file',
145+
'background-audio',
131146
# enter time values
132147
'date',
133148
'datetime',
134149
'time',
135-
136150
# prompt to collect geo data
137151
'location',
138-
139152
# no response
140153
'acknowledge',
141154
'note',
155+
# external data source
156+
'xml-external',
157+
'csv-external',
158+
# other
159+
'range',
160+
'hidden',
142161
] + GEO_TYPES
143162
formpack_preferred_types = set(MAIN_TYPES + LABEL_OPTIONAL_TYPES + SELECT_TYPES)
144163

src/formpack/utils/xform_tools.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
("select one from", 'select_one'),
1616
("select1", 'select_one'),
1717
("select one", 'select_one'),
18+
('select one from file', 'select_one_from_file'),
1819
("add select multiple prompt using", 'select_multiple'),
1920
("select all that apply from", 'select_multiple'),
2021
("select multiple", 'select_multiple'),
2122
("select all that apply", 'select_multiple'),
23+
('select multiple from file', 'select_multiple_from_file'),
2224
("select_one_external", "select one external"),
2325
('cascading select', 'cascading_select'),
2426
('location', 'geopoint'),

tests/test_replace_aliases.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ def test_select_one_aliases_replaced():
2121
assert dealias_type('select_one dogs') == 'select_one dogs'
2222

2323

24+
def test_replace_select_one_from_file():
25+
s1 = {'survey': [{'type': 'select one from file dogs.csv'}]}
26+
replace_aliases(s1, in_place=True)
27+
assert s1['survey'][0]['type'] == 'select_one_from_file dogs.csv'
28+
29+
2430
def test_true_false_value_replaced():
2531
# only replaced on columns with TF_COLUMNS
2632
s1 = {'survey': [
@@ -43,6 +49,12 @@ def test_select_multiple_aliases_replaced():
4349
assert dealias_type('select_multiple dogs') == 'select_multiple dogs'
4450

4551

52+
def test_replace_select_multiple_from_file():
53+
s1 = {'survey': [{'type': 'select multiple from file dogs.csv'}]}
54+
replace_aliases(s1, in_place=True)
55+
assert s1['survey'][0]['type'] == 'select_multiple_from_file dogs.csv'
56+
57+
4658
def test_misc_types():
4759
assert dealias_type('begin group') == 'begin_group'
4860
assert dealias_type('end group') == 'end_group'

tests/test_utils_flatten_content.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,27 @@ def test_flatten_select_multiple_type():
133133
assert ss_struct[0]['type'] == 'select_multiple yn'
134134

135135

136+
def test_flatten_select_one_from_file_type():
137+
a1 = _wrap_type({'select_one_from_file': 'yn.csv'})
138+
flatten_content(a1, in_place=True)
139+
ss_struct = a1['survey']
140+
assert ss_struct[0]['type'] == 'select_one_from_file yn.csv'
141+
142+
143+
def test_flatten_select_multiple_from_file_type():
144+
a1 = _wrap_type({'select_multiple_from_file': 'yn.csv'})
145+
flatten_content(a1, in_place=True)
146+
ss_struct = a1['survey']
147+
assert ss_struct[0]['type'] == 'select_multiple_from_file yn.csv'
148+
149+
150+
def test_flatten_rank_type():
151+
a1 = _wrap_type({'rank': 'yn'})
152+
flatten_content(a1, in_place=True)
153+
ss_struct = a1['survey']
154+
assert ss_struct[0]['type'] == 'rank yn'
155+
156+
136157
def test_json_hash():
137158
# consistent output
138159
assert json_hash({'a': 'z', 'b': 'y', 'c': 'x'}) == 'f6117d60'

0 commit comments

Comments
 (0)