Skip to content
This repository has been archived by the owner on Apr 5, 2019. It is now read-only.

Commit

Permalink
Support importing XLSForms from file-like objects in addition to file…
Browse files Browse the repository at this point in the history
… paths.
  • Loading branch information
Esmail committed Oct 8, 2014
1 parent b55b86f commit 0263c32
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 19 deletions.
16 changes: 12 additions & 4 deletions pyxform/survey_from.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,32 +42,40 @@ def xls(path=None, filelike_obj=None, warnings=None):
:param str path: Path to the input file.
:param filelike_obj: Optional file-like object from which to import the survey.
:type filelike_obj: file or StringIO.StringIO or other type supported by lxml.etree.parse().
:type filelike_obj: file or StringIO.StringIO or other type that has a 'read()' method.
:param list warnings: Optional list into which any warnings generated during import will be appended.
:rtype: pyxform.survey.Survey
'''

# TODO: Import XLSForms directly to 'Survey' objects.
# Convert the XLS to JSON then import the JSON ...such a kludge.
workbook_dict= pyxform.xls2json_backends.xls_to_dict(path)
if path:
workbook_dict= pyxform.xls2json_backends.xls_to_dict(path)
elif filelike_obj:
workbook_dict= pyxform.xls2json_backends.xls_to_dict(filelike_obj)
json_temp= pyxform.xls2json.workbook_to_json(workbook_dict, warnings=warnings)
survey= pyxform.builder.create_survey_element_from_dict(json_temp)

return survey


def csv(path=None, filey_obj=None, warnings=None):
def csv(path=None, filelike_obj=None, warnings=None):
'''
Construct a 'Survey' object from an CSV-formatted XLSForm.
:param str xls_in_path: Path to the input file.
:param filelike_obj: Optional file-like object from which to import the survey.
:type filelike_obj: file or StringIO.StringIO or other type that has a 'read()' method.
:param list warnings: Optional list into which any warnings generated during import will be appended.
:rtype: pyxform.survey.Survey
'''

# TODO: Import XLSForms directly to 'Survey' objects.
# Convert the CSV to JSON then import the JSON ...such a kludge.
workbook_dict= pyxform.xls2json_backends.csv_to_dict(path)
if path:
workbook_dict= pyxform.xls2json_backends.csv_to_dict(path)
elif filelike_obj:
workbook_dict= pyxform.xls2json_backends.csv_to_dict(filelike_obj)
json_temp= pyxform.xls2json.workbook_to_json(workbook_dict, warnings=warnings)
survey= pyxform.builder.create_survey_element_from_dict(json_temp)

Expand Down
45 changes: 33 additions & 12 deletions pyxform/survey_to_xlsform.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import base64
import re
import os
import cStringIO
from tempfile import NamedTemporaryFile

import pandas

Expand Down Expand Up @@ -236,39 +238,50 @@ def get_survey_element_label(survey_element):
return labels


def to_xls(survey, out_file_path, warnings=None):
def to_xls(survey, path=None, warnings=None):
'''
Convert the provided survey to a XLS-encoded XForm.
:param pyxform.survey.Survey survey:
:param str out_file_path: Filesystem path to the desired output file.
:param str path: Optional filesystem path to the desired output file.
:param list warnings: Optional list into which any warnings generated during export will be appended.
'''

# Organize the data for spreadsheet output.
sheet_dfs= XlsFormExporter(survey, warnings).sheet_dfs

xls_writer= pandas.ExcelWriter(out_file_path, encoding='UTF-8')
# Create if a permanent file with a filesystem path isn't desired.
temp_file= None
if not path:
temp_file= NamedTemporaryFile(suffix='-pyxform.xls')
path= temp_file.name

# Write out the data sheet-by-sheet.
xls_writer= pandas.ExcelWriter(path, encoding='UTF-8')
for sheet_name, df in sheet_dfs.iteritems():
df.to_excel(xls_writer, sheet_name, index=False)

xls_writer.save()

# If a permanent file wasn't desired, to the beginning of the temp. file and return it.
if temp_file:
#temp_file.file.seek(0) # Necessary?
return temp_file



def to_csv(survey, out_file_path, warnings=None):
def to_csv(survey, path=None, warnings=None):
'''
Convert the provided survey to a CSV-formatted XForm.
:param pyxform.survey.Survey survey:
:param str out_file_path: Filesystem path to the desired output file.
:param str path: Optional filesystem path to the desired output file.
:param list warnings: Optional list into which any warnings generated during export will be appended.
'''

# Organize the data for spreadsheet output.
sheet_dfs= XlsFormExporter(survey, warnings).sheet_dfs

# Reorganize the data into multi-"sheet" CSV form and export.
csv_buffer= str()
csv_buffer= cStringIO.StringIO()
for sheet_name, df in sheet_dfs.iteritems():
# Prepend a row of the column names into the sheet.
csv_df= pandas.concat([pandas.DataFrame(df.columns.to_series()).T, df])
Expand All @@ -277,8 +290,16 @@ def to_csv(survey, out_file_path, warnings=None):
# Move the 'sheet' column to the front.
csv_df= csv_df[['sheet']+csv_df.columns.drop('sheet').tolist()]

csv_buffer+= csv_df.to_csv(header=False, index=False, encoding='UTF-8')
csv_buffer.write(csv_df.to_csv(header=False, index=False, encoding='UTF-8'))
csv_buffer.seek(0)

if path:
f=open(path,'w')
f.write(csv_buffer.read())
else:
return csv_buffer

with open(out_file_path,'w') as f:
f.write(csv_buffer)

if __name__ == '__main__':
import pyxform.survey_from
open('/home/esmail/all_question_types_survey_kf1.csv','w').write(to_csv(pyxform.survey_from.xform('/home/esmail/programming/pyxform/pyxform/tests/example_xforms/all_question_types_survey_kf1.xml')).read())
# open('/home/esmail/test.xls','w').write(to_xls(pyxform.survey_from.xform('/home/esmail/programming/pyxform/pyxform/tests/example_xforms/all_question_types_survey_kf1.xml')).read())
32 changes: 29 additions & 3 deletions pyxform/tests/test_survey_to_xlsform.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import unittest
import os.path
import tempfile
from tempfile import NamedTemporaryFile

from pyxform import survey_from
from pyxform import constants
Expand Down Expand Up @@ -41,7 +41,7 @@ def _export_and_reimport(original_survey, export_format='xls', warnings=None):
:rtype: pyxform.survey.Survey
'''

with tempfile.NamedTemporaryFile(suffix='-pyxform.'+ export_format) as xlsform_tempfile:
with NamedTemporaryFile(suffix='-pyxform.'+ export_format) as xlsform_tempfile:
# Export the survey to file and re-import it.
if export_format.lower() == 'xls':
original_survey.to_xls(xlsform_tempfile.name, warnings=warnings)
Expand All @@ -54,7 +54,7 @@ def _export_and_reimport(original_survey, export_format='xls', warnings=None):
reimported_survey= survey_from.xform(xlsform_tempfile, warnings=warnings)

return reimported_survey


def test_consistent_export(self):
'''
Expand Down Expand Up @@ -226,6 +226,32 @@ def test_cascading_select_graceful_failure(self):
self.assertEqual(question_choices[0]['label'], XlsFormExporter.CASCADING_SELECT_SAD_CHOICE_LABEL)


def test_import_export_file_obj(self):
'''
Test that XLSForms can be imported from and exported to file objects.
'''

xls_survey_path= os.path.join(self.xform_directory_path, '../example_xls/new_cascading_select_xlsform.org.xlsx')
xls_survey_from_path= survey_from.xls(path=xls_survey_path)
xls_survey_from_file_obj= survey_from.xls(filelike_obj=open(xls_survey_path))
self.assertEqual(xls_survey_from_path, xls_survey_from_file_obj)

csv_temp_file= NamedTemporaryFile(suffix='-pyxform.csv')
xls_survey_from_path.to_csv(path=csv_temp_file.name)
csv_survey_from_path= survey_from.csv(path=csv_temp_file.name)
csv_survey_from_file_obj= survey_from.csv(filelike_obj=open(csv_temp_file.name))
self.assertEqual(csv_survey_from_path, csv_survey_from_file_obj)

# TODO: Pending modifications in 'Survey.to_X'
# xls_filelike_obj= xls_survey_from_path.to_xls()
# xls_survey_reimport= survey_from.xls(filelike_obj=xls_filelike_obj)
# self.assertEqual(xls_survey_from_path, xls_survey_reimport)
#
# csv_filelike_obj= csv_survey_from_path.to_xls()
# csv_survey_reimport= survey_from.csv(filelike_obj=csv_filelike_obj)
# self.assertEqual(csv_survey_from_path, csv_survey_reimport)


if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()

0 comments on commit 0263c32

Please sign in to comment.