Skip to content

Commit

Permalink
Merge pull request #63 from lindsay-stevens/no-validate-arg
Browse files Browse the repository at this point in the history
"--skip_validate" arg for xls2xform.py
  • Loading branch information
dorey committed May 3, 2016
2 parents bb756c4 + c758940 commit e33e260
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 24 deletions.
Binary file modified pyxform/odk_validate/ODK_Validate.jar
Binary file not shown.
23 changes: 20 additions & 3 deletions pyxform/odk_validate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,14 @@ def _kill_process_after_a_timeout(pid):
return (p.returncode, timeout, stdout, stderr)

def _java_installed():
p = Popen(["which","java"], stdout=PIPE)
return len(p.stdout.readlines()) != 0
# This alternative allows for java detection on Windows.
try:
p = Popen(["which", "java"], stdout=PIPE).stdout.readlines()
found = len(p) != 0
except WindowsError:
p = Popen('java -version', stderr=PIPE, stdout=PIPE).stderr.read()
found = p.startswith('java version')
return found

def _cleanup_errors(error_message):
def get_last_item(xpathStr):
Expand Down Expand Up @@ -106,7 +112,18 @@ def check_xform(path_to_xform):
returncode, timeout, stdout, stderr = run_popen_with_timeout(
["java", "-jar", ODK_VALIDATE_JAR, path_to_xform], 100)
warnings = []
stderr = stderr.decode('utf-8')
# On Windows, stderr may be latin-1; in which case utf-8 decode will fail.
# If both utf-8 and latin-1 decoding fail then raise all as IOError.
# If the above validate jar call fails, add make sure that the java path
# is set, e.g. PATH=C:\Program Files (x86)\Java\jre1.8.0_71\bin
try:
stderr = stderr.decode('utf-8')
except UnicodeDecodeError as ude:
try:
stderr = stderr.decode('latin-1')
except BaseException as be:
msg = "Failed to decode validate stderr as utf-8 or latin-1."
raise IOError(msg, ude, be)

if timeout:
return ["XForm took to long to completely validate."]
Expand Down
17 changes: 12 additions & 5 deletions pyxform/survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from survey_element import SurveyElement
from errors import PyXFormError
import constants
import os


nsmap = {
Expand Down Expand Up @@ -466,16 +467,22 @@ def print_xform_to_file(self, path=None, validate=True, warnings=None):
warnings = []
if not path:
path = self._print_name + ".xml"
fp = codecs.open(path, mode="w", encoding="utf-8")
fp.write(self._to_pretty_xml())
fp.close()
with codecs.open(path, mode="w", encoding="utf-8") as fp:
fp.write(self._to_pretty_xml())
if validate:
warnings.extend(check_xform(path))

def to_xml(self, validate=True, warnings=None):
with tempfile.NamedTemporaryFile() as tmp:
# On Windows, NamedTemporaryFile must be opened exclusively.
# So it must be explicitly created, opened, closed, and removed.
tmp = tempfile.NamedTemporaryFile(delete=False)
tmp.close()
try:
# this will throw an exception if the xml is not valid
self.print_xform_to_file(tmp.name, validate=validate, warnings=warnings)
self.print_xform_to_file(tmp.name, validate=validate,
warnings=warnings)
finally:
os.remove(tmp.name)
return self._to_pretty_xml()

def instantiate(self):
Expand Down
37 changes: 36 additions & 1 deletion pyxform/tests/xls2xform_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from unittest import TestCase
import pyxform
from pyxform.xls2xform import _create_parser


class XLS2XFormTests(TestCase):
Expand All @@ -24,4 +25,38 @@ class XLS2XFormTests(TestCase):
},
'title': u'test'
}
survey = pyxform.create_survey(**survey_package)
survey = pyxform.create_survey(**survey_package)

def test_create_parser_without_args(self):
"""Should exit when no args provided."""
with self.assertRaises(SystemExit):
_create_parser().parse_args([])

def test_create_parser_with_args(self):
"""Should parse the provided arguments."""
arg_xlsform = 'xlsform.xlsx'
arg_output = '.'
arg_json = '--json'
arg_skip_validate = '--skip_validate'
arg_list = [arg_json, arg_skip_validate, arg_xlsform, arg_output]
args = _create_parser().parse_args(arg_list)
self.assertEqual(arg_xlsform, args.path_to_XLSForm)
self.assertEqual(arg_output, args.output_path)
self.assertEqual(True, args.json)
self.assertEqual(False, args.skip_validate)

def test_create_parser_json_default_false(self):
"""Should have json=False if not specified."""
arg_xlsform = 'xlsform.xlsx'
arg_output = '.'
arg_list = [arg_xlsform, arg_output]
args = _create_parser().parse_args(arg_list)
self.assertEqual(False, args.json)

def test_create_parser_no_validate_default_true(self):
"""Should have no_validate=True if not specified."""
arg_xlsform = 'xlsform.xlsx'
arg_output = '.'
arg_list = [arg_xlsform, arg_output]
args = _create_parser().parse_args(arg_list)
self.assertEqual(True, args.skip_validate)
23 changes: 17 additions & 6 deletions pyxform/tests_v1/pyxform_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pyxform.xls2json import workbook_to_json
from pyxform.builder import create_survey_element_from_dict
from pyxform.odk_validate import check_xform, ODKValidateError
import os


class PyxformTestError(Exception):
Expand Down Expand Up @@ -88,13 +89,23 @@ def _pull_xml_node_from_root(element_selector):
if debug:
print xml
if run_odk_validate:
with tempfile.NamedTemporaryFile(suffix='.xml') as tmp:
fp = codecs.open(tmp.name, mode="w", encoding="utf-8")
if '_xml_append' in kwargs:
xml += kwargs['_xml_append']
fp.write(xml)
fp.close()
# On Windows, NamedTemporaryFile must be opened exclusively.
# So it must be explicitly created, opened, closed, and removed.
tmp = tempfile.NamedTemporaryFile(suffix='.xml', delete=False)
tmp.close()
try:
with codecs.open(
tmp.name, mode="w", encoding="utf-8") as fp:
if '_xml_append' in kwargs:
xml += kwargs['_xml_append']
fp.write(xml)
fp.close()
check_xform(tmp.name)
finally:
# Clean up the temporary file
os.remove(tmp.name)
assert not os.path.isfile(tmp.name)

if len(odk_validate_error__contains) > 0:
raise PyxformTestError(
"ODKValidateError was not raised"
Expand Down
43 changes: 34 additions & 9 deletions pyxform/xls2xform.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from utils import sheet_to_csv, has_external_choices
import os

def xls2xform_convert(xlsform_path, xform_path):

def xls2xform_convert(xlsform_path, xform_path, validate=True):
warnings = []

json_survey = xls2json.parse_file_to_json(xlsform_path, warnings=warnings)
Expand All @@ -19,7 +20,7 @@ def xls2xform_convert(xlsform_path, xform_path):
# ODK Validate.
# This may be desirable since ODK Validate requires launching a subprocess
# that runs some java code.
survey.print_xform_to_file(xform_path, validate=True, warnings=warnings)
survey.print_xform_to_file(xform_path, validate=validate, warnings=warnings)
output_dir = os.path.split(xform_path)[0]
if has_external_choices(json_survey):
itemsets_csv = os.path.join(output_dir, "itemsets.csv")
Expand All @@ -31,13 +32,31 @@ def xls2xform_convert(xlsform_path, xform_path):
return warnings


if __name__ == '__main__':
def _create_parser():
"""
Parse command line arguments.
"""
parser = argparse.ArgumentParser()
parser.add_argument('path_to_XLSForm')
parser.add_argument('output_path')
parser.add_argument('--json',
action='store_true',
parser.add_argument(
"path_to_XLSForm",
help="Path to the Excel XSLX file with the XLSForm definition.")
parser.add_argument(
"output_path",
help="Path to save the output to.")
parser.add_argument(
"--json",
action="store_true",
help="Capture everything and report in JSON format.")
parser.add_argument(
"--skip_validate",
action="store_false",
default=True,
help="Skip default running of ODK Validate on the output XForm XML.")
return parser


def main_cli():
parser = _create_parser()
args = parser.parse_args()

if args.json:
Expand All @@ -46,7 +65,8 @@ def xls2xform_convert(xlsform_path, xform_path):
response = {'code': None, 'message': None, 'warnings': []}

try:
response['warnings'] = xls2xform_convert(args.path_to_XLSForm, args.output_path)
response['warnings'] = xls2xform_convert(
args.path_to_XLSForm, args.output_path, args.skip_validate)

response['code'] = 100
response['message'] = "Ok!"
Expand All @@ -62,8 +82,13 @@ def xls2xform_convert(xlsform_path, xform_path):

print json.dumps(response)
else:
warnings = xls2xform_convert(args.path_to_XLSForm, args.output_path)
warnings = xls2xform_convert(
args.path_to_XLSForm, args.output_path, args.skip_validate)
if len(warnings) > 0: print "Warnings:"
for w in warnings:
print w
print 'Conversion complete!'


if __name__ == '__main__':
main_cli()

0 comments on commit e33e260

Please sign in to comment.