Hi all,
I'm working on some reports that use text boxes with their image content
input as strings and would like to be able to use the CLI filters based
on the string content. However, it looks like the
Locals.__getitem___(self, key) method in sdaps.clifilter boxes (haha)
the filter expressions into using the return value of
qobjects[key].get_answer() which, for a Text Question, is always a bool
(see class Text in sdaps.model.questionnaire ). Unless I'm missing
something this looks like there's no existing capability to filter on
the string of the response. Two questions:
1) Is there something I'm missing that already does this?
2) If not, is there any suggestion on the most elegant way to
incorporate this into filters?
My initial hack is to add an additional check to
Locals.__getitem__(self, key) that use a regex to search for
"csvtext(_[0-9_]+)" and and then take the group from the match
(_[0-9_]+), look for a matching key in self.qobjects, and returns the
text or array of text. I do not think that will break anyone else's
filters, but also do not understand this codebase as well as some of you
might. I assume there may also be a more elegant solution.
If there's any feedback on this I will revise and pull-request the patches.
Thanks for your thoughts,
Matthew Roy
The relevant existing code for filtering:
========== sdaps.clifilter
class Locals(object):
def __init__(self, survey):
self.survey = survey
self.qobjects = dict([
(qobject.id_filter(), qobject)
for qobject in self.survey.questionnaire.qobjects
])
def __getitem__(self, key):
if key in self.qobjects:
return self.qobjects[key].get_answer()
elif key in ['survey_id', 'questionnaire_id', 'global_id',
'valid', 'quality', 'recognized', 'verified', 'complete']:
return getattr(self.survey.sheet, key)
else:
raise KeyError
def clifilter(survey, expression):
if expression is None or expression.strip() == '':
return lambda: True
exp = compile(expression, '<string>', 'eval')
globals = __builtins__
locals = Locals(survey)
return lambda: eval(exp, globals, locals)
========== sdaps.model.questionnaire
class Text(Question):
def __unicode__(self):
return unicode().join(
[Question.__unicode__(self)] +
[unicode(box) for box in self.boxes]
)
def get_answer(self):
'''it's a bool, wether there is content in the textbox
'''
assert len(self.boxes) == 1
return self.boxes[0].data.state
=== proposed sdaps.clifilter.Locals.__getitem__(self, key)
def __getitem__(self, key):
if key in self.qobjects:
return self.qobjects[key].get_answer()
elif key in ['survey_id', 'questionnaire_id', 'global_id',
'valid', 'quality', 'recognized', 'verified', 'complete']:
return getattr(self.survey.sheet, key)
else:
#try to match the "reporttext" "csvtext" filter parameters
match = re.match('reporttext(_[0-9_]+)', key)
if match and match.group(1) in self.qobjects:
text = ""
for box in self.qobjects[match.group(1)].boxes:
text = text + box.data.text
return text
match = re.match('csvtext(_[0-9_]+)', key)
if match and match.group(1) in self.qobjects:
return self.qobjects[match.group(1)].csvdata.export_data()
#If nothing worked, raise a KeyError
raise KeyError
=== proposed usage
#csvexport will return a dictionary of text by sub-box
sdaps PROJECT csv export --filter "'TERM' in csvtext_1_7['1_7_0']"
#reporttext will just append them all together
sdaps PROJECT report --filter "'TERM' in reporttext_1_7"
--
Matthew Roy
TurnAround Factor
Richmond, Virginia