mangroveorg/datawinners

View on GitHub
datawinners/blue/view.py

Summary

Maintainability
F
1 wk
Test Coverage
import json
import logging
import mimetypes
import os
import re
import traceback
from copy import deepcopy
from tempfile import NamedTemporaryFile

from django.contrib.auth.decorators import login_required
from django.core.mail import EmailMessage
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from django.template.defaultfilters import slugify
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_view_exempt, csrf_response_exempt, csrf_exempt
from django.views.generic.base import View
from datawinners.blue.xform_edit.submission import Submission
from mangrove.datastore.entity_type import get_unique_id_types

from pyxform.errors import PyXFormError, BindError

from mangrove.errors.MangroveException import ExceedSubmissionLimitException, QuestionAlreadyExistsException
from mangrove.errors.MangroveException import QuestionCodeAlreadyExistsException
from mangrove.form_model.form_model import get_form_model_by_code
from mangrove.form_model.project import Project
from mangrove.transport.repository.survey_responses import get_survey_response_by_id, get_survey_responses, \
    survey_responses_by_form_model_id
from mangrove.utils.dates import py_datetime_to_js_datestring
from mangrove.transport.xforms.xform import itemset_for
from datawinners import settings
from datawinners.accountmanagement.decorators import session_not_expired, is_not_expired, is_datasender_allowed, \
    project_has_web_device, is_datasender
from datawinners.accountmanagement.models import Organization
from datawinners.activitylog.models import UserActivityLog
from datawinners.blue.error_translation_utils import transform_error_message, translate_odk_message
from datawinners.blue.rules.factory import get_all_rules
from datawinners.blue.xform_bridge import MangroveService, XlsFormParser, XFormTransformer, XFormSubmissionProcessor, \
    get_generated_xform_id_name, XFormImageProcessor
from datawinners.blue.xform_edit.questionnaire import Questionnaire
from datawinners.blue.xform_edit.validator import Validator
from datawinners.blue.xform_editor import XFormEditor, UnsupportedXformEditException
from datawinners.blue.xform_web_submission_handler import XFormWebSubmissionHandler
from datawinners.common.constant import EDITED_DATA_SUBMISSION_ADV_QUEST, EDITED_QUESTIONNAIRE
from datawinners.feeds.database import get_feeds_database
from datawinners.main.database import get_database_manager
from datawinners.main.utils import get_database_name
from datawinners.project.helper import generate_questionnaire_code, is_project_exist
from datawinners.project.utils import is_quota_reached
from datawinners.project.views.utils import get_form_context
from datawinners.project.views.views import SurveyWebQuestionnaireRequest
from datawinners.questionnaire.questionnaire_builder import QuestionnaireBuilder
from datawinners.search.submission_index import SubmissionSearchStore
from datawinners.settings import EMAIL_HOST_USER, HNI_SUPPORT_EMAIL_ID
from collections import OrderedDict
from datawinners.blue.xlsform_utils import convert_excel_to_dict, \
    convert_json_to_excel, purify_posted_data
from datawinners.accountmanagement.decorators import restrict_access

logger = logging.getLogger("datawinners.xls-questionnaire")

QUESTIONNAIRE_AS_DICT_FOR_BUILDER = 'questionnaire_as_dict_for_builder'


class ProjectUpload(View):
    @method_decorator(csrf_view_exempt)
    @method_decorator(csrf_response_exempt)
    @method_decorator(login_required)
    @method_decorator(session_not_expired)
    @method_decorator(is_not_expired)
    def dispatch(self, *args, **kwargs):
        return super(ProjectUpload, self).dispatch(*args, **kwargs)

    def post(self, request):
        project_name = request.GET['pname'].strip()
        manager = get_database_manager(request.user)

        try:
            xls_parser_response = _try_parse_xls(manager, request, project_name)

            if isinstance(xls_parser_response, HttpResponse):
                return xls_parser_response

            send_email_if_unique_id_type_question_has_no_registered_unique_ids(xls_parser_response, request,
                                                                               project_name)
            questionnaire_code = generate_questionnaire_code(manager)
            excel_file = _temp_file(request)
            mangrove_service = MangroveService(request, questionnaire_code=questionnaire_code,
                                               project_name=project_name, xls_form=excel_file,
                                               xls_parser_response=xls_parser_response)
            questionnaire_id, form_code = mangrove_service.create_project()

            if not questionnaire_id:
                logger.info("User: %s. Upload Error: %s", request.user.username, "Questionnaire must be unique")

                return HttpResponse(content_type='application/json', content=json.dumps({
                    'success': False,
                    'error_msg': [_("Duplicate questionnaire name. All questionnaire (name) must be unique.")],
                    'message_prefix': _("Sorry! Current version of DataWinners does not support"),
                    'message_suffix': _("Use another questionnaire name and upload again.")
                }))

            questionnaire = Project.get(manager, questionnaire_id)
            _save_questionnaire_as_dict_for_builder(questionnaire, excel_file=excel_file)
        except QuestionAlreadyExistsException as e:
            return HttpResponse(content_type='application/json', content=json.dumps({
                'success': False,
                'error_msg': [
                    _(e.message)
                ]
            }))

        except QuestionCodeAlreadyExistsException as e:
            return HttpResponse(content_type='application/json', content=json.dumps({
                'success': False,
                'error_msg': [
                    _(e.message) % e.data[0]
                ]
            }))
        
        return HttpResponse(
            json.dumps(
                {
                    "success": True,
                    "project_name": project_name,
                    "project_id": questionnaire_id,
                    "form_code": form_code
                }),
            content_type='application/json')


class ProjectBuilder(View):
    def _get_pre_computed_questionnaire_as_dict(self, questionnaire):
        try:
            return json.loads(questionnaire.get_attachments(QUESTIONNAIRE_AS_DICT_FOR_BUILDER),
                              object_pairs_hook=OrderedDict)
        except Exception as e:
            # Expected exception, if file not exist
            logger.info(e.message)
        return None

    def _reload_required(self, request):
        reload = request.GET.get("reload", 'false').lower().strip()
        return reload == 'true'

    def get(self, request, project_id):
        manager = get_database_manager(request.user)
        questionnaire = Project.get(manager, project_id)
        excel_as_dict = None
        try:
            if not self._reload_required(request):  # Switch to handle unexpected failures
                excel_as_dict = self._get_pre_computed_questionnaire_as_dict(questionnaire)
            raw_excel, file_type = questionnaire.has_questionnaire_attachment()[1:]
            if excel_as_dict is None:
                excel_as_dict = convert_excel_to_dict(file_content=raw_excel, file_type=file_type)
                _save_questionnaire_as_dict_for_builder(questionnaire, excel_as_dict)

            return HttpResponse(
                json.dumps(
                    {
                        "project_id": project_id,
                        "questionnaire": excel_as_dict,
                        "file_type": file_type,
                        "unique_id_types": get_unique_id_types(manager)
                    }),
                content_type='application/json')
        except Exception as e:
            logger.exception("Exception : \n%s" % e)
            return HttpResponse(
                json.dumps(
                    {
                        "success": False,
                        "project_id": project_id,
                        'reason': 'Unable to fetch questionnaire details',
                        'details': e.message

                    }),
                content_type='application/json')

    def post(self, request, project_id):
        data = request.POST['data']
        file_type = request.POST['file_type']
        is_draft = request.POST['is_draft']
        try:
            excel_as_dict = json.loads(data, object_pairs_hook=OrderedDict)
            excel_as_dict = purify_posted_data(excel_as_dict)
            if is_draft and is_draft.lower().strip() == 'true':
                manager = get_database_manager(request.user)
                questionnaire = Project.get(manager, project_id)
                _save_questionnaire_as_dict_for_builder(questionnaire, excel_as_dict=excel_as_dict)
                return HttpResponse(
                    json.dumps(
                        {
                            "status": "success",
                            "project_id": project_id,
                            'reason': 'Successfully updated',
                            'details': ''

                        }),
                    content_type='application/json')

            excel_raw_stream = convert_json_to_excel(excel_as_dict, file_type)
            excel_file = _temp_file(request, excel_raw_stream, file_type)
            return _edit_questionnaire(request, project_id, excel_file, excel_as_dict)

        except Exception as e:
            logger.exception('Unable to save questionnaire from builder')
            return HttpResponse(
                json.dumps(
                    {
                        "status": "error",
                        "project_id": project_id,
                        'reason': 'Unable to save',  # TODO: i18n translation
                        'details': e.message

                    }),
                content_type='application/json')


def _save_questionnaire_as_dict_for_builder(questionnaire, excel_as_dict=None, excel_file=None):
    try:
        if excel_as_dict is None:
            extension = os.path.splitext(excel_file.name)[1]
            if excel_as_dict is None:
                excel_file.seek(0)
                excel_as_dict = convert_excel_to_dict(file_content=excel_file.read(), file_type=extension[1:])

        questionnaire.update_attachments(excel_as_dict, attachment_name=QUESTIONNAIRE_AS_DICT_FOR_BUILDER)
    except:
        logger.exception('Unable to pre compute excel as json for Builder')


def _edit_questionnaire(request, project_id, excel_file=None, excel_as_dict=None):
    manager = get_database_manager(request.user)
    questionnaire = Project.get(manager, project_id)
    try:
        xls_parser_response = _try_parse_xls(manager, request, questionnaire.name, excel_file)

        if isinstance(xls_parser_response, HttpResponse):
            return xls_parser_response

        doc = deepcopy(questionnaire._doc)
        doc.xform = MangroveService(request, questionnaire_code=questionnaire.form_code,
                                    xls_parser_response=xls_parser_response).xform_with_form_code
        new_questionnaire = Project.new_from_doc(manager, doc)
        QuestionnaireBuilder(new_questionnaire, manager).update_questionnaire_with_questions(
            xls_parser_response.json_xform_data)
        xform_rules = get_all_rules()
        if excel_file is None:
            excel_file = _temp_file(request)
        else:
            excel_file.seek(0)
        questionnaire_wrapper = Questionnaire(excel_file)
        activity_log_detail = {}
        XFormEditor(Submission(manager, get_database_name(request.user), xform_rules), Validator(xform_rules),
                    questionnaire_wrapper).edit(new_questionnaire, questionnaire, activity_log_detail)
        questionnaire = Project.get(manager, project_id)
        _save_questionnaire_as_dict_for_builder(questionnaire, excel_as_dict, excel_file)
        UserActivityLog().log(request, action=EDITED_QUESTIONNAIRE, project=questionnaire.name,
                              detail=json.dumps(activity_log_detail))

    except UnsupportedXformEditException as e:
        return HttpResponse(content_type='application/json', content=json.dumps({
            'success': False,
            'unsupported': True,
            'error_msg': [
                _("Unsupported edit operation")
            ],
            "status": "error",
            'reason': "Unsupported edit operation",  # TODO: i18n translation
            'details': e.message
        }))
    except QuestionAlreadyExistsException as e:
        return HttpResponse(content_type='application/json', content=json.dumps({
            'success': False,
            'error_msg': [
                _(e.message)
            ],
            "status": "error",
            'reason': "Save Failed",  # TODO: i18n translation
            'details': _(e.message)
        }))
    return HttpResponse(
        json.dumps({
            "success": True,
            "status": "success",
            'reason': 'Successfully updated'  # TODO: i18n translation
        }),
        content_type='application/json'
    )


class ProjectUpdate(View):
    @method_decorator(csrf_view_exempt)
    @method_decorator(csrf_response_exempt)
    @method_decorator(login_required)
    @method_decorator(session_not_expired)
    @method_decorator(is_not_expired)
    @method_decorator(is_datasender)
    def dispatch(self, *args, **kwargs):
        return super(ProjectUpdate, self).dispatch(*args, **kwargs)

    def recreate_submissions_mapping(self, manager, questionnaire):
        SubmissionSearchStore(manager, questionnaire, None).recreate_elastic_store()

    def post(self, request, project_id):
        if request.GET["edit"] == 'true':
            return _edit_questionnaire(request, project_id)

        return self._overwrite(project_id, request)

    def _overwrite(self, project_id, request):
        try:
            manager = get_database_manager(request.user)
            questionnaire = Project.get(manager, project_id)
            xls_parser_response = _try_parse_xls(manager, request, questionnaire.name)
            if isinstance(xls_parser_response, HttpResponse):
                return xls_parser_response
            send_email_if_unique_id_type_question_has_no_registered_unique_ids(xls_parser_response, request,
                                                                               questionnaire.name)
            mangrove_service = MangroveService(request, questionnaire_code=questionnaire.form_code,
                                               project_name=questionnaire.name, xls_parser_response=xls_parser_response)
            questionnaire.xform = mangrove_service.xform_with_form_code
            QuestionnaireBuilder(questionnaire, manager).update_questionnaire_with_questions(
                xls_parser_response.json_xform_data)
            questionnaire.update_media_field_flag()
            questionnaire.save(process_post_update=False)
            UserActivityLog().log(self.request, action=EDITED_QUESTIONNAIRE, project=questionnaire.name,
                                  detail=questionnaire.name)
            tmp_file = _temp_file(request)
            base_name, extension = os.path.splitext(tmp_file.name)
            questionnaire.update_attachments(tmp_file, 'questionnaire%s' % extension)
            questionnaire.update_external_itemset(mangrove_service.itemsets_csv)
            self._purge_submissions(manager, questionnaire)
            self._purge_feed_documents(questionnaire, request)
            self._purge_media_details_documents(manager, questionnaire)
            self.recreate_submissions_mapping(manager, questionnaire)
            excel_file = _temp_file(request)
            excel_file.seek(0)
            _save_questionnaire_as_dict_for_builder(questionnaire, excel_file=excel_file)
            if xls_parser_response.info:
                info_list = list(xls_parser_response.info)
                logger.info("User: %s. Edit upload Errors: %s", request.user.username, json.dumps(info_list))
                return HttpResponse(content_type='application/json', content=json.dumps({
                    'success': True,
                    'information': info_list,
                }))

        except QuestionAlreadyExistsException as e:
            return HttpResponse(content_type='application/json', content=json.dumps({
                'success': False,
                'error_msg': [
                    _(e.message)
                ],
                "status": "error",
                'reason': "Save Failed",  # TODO: i18n translation
                'details': _(e.message)
            }))
        except QuestionCodeAlreadyExistsException as e:
            return HttpResponse(content_type='application/json', content=json.dumps({
                'success': False,
                'error_msg': [
                    _(e.message) % e.data[0]
                ]
            }))



        return HttpResponse(
            json.dumps(
                {
                    "success": True,
                    "project_name": questionnaire.name,
                    "project_id": questionnaire.id,
                    "file_name": "%s%s" % (slugify(questionnaire.name), extension),
                    # "xls_dict": XlsProjectParser().parse(file_content)
                }),
            content_type='application/json')

    def _purge_feed_documents(self, questionnaire, request):
        feed_dbm = get_feeds_database(request.user)
        rows = feed_dbm.view.questionnaire_feed(startkey=[questionnaire.form_code],
                                                endkey=[questionnaire.form_code, {}], include_docs=True)
        for row in rows:
            feed_dbm.database.delete(row['doc'])

    def _purge_submissions(self, manager, questionnaire):
        survey_responses = survey_responses_by_form_model_id(manager, questionnaire.id)
        for survey_response in survey_responses:
            data_record = survey_response.data_record
            if data_record:
                data_record.delete()

            survey_response.delete()

    def _purge_media_details_documents(self, dbm, questionnaire):
        media_details_docs = dbm.database.iterview("all_media_details/all_media_details", 1000, reduce=False,
                                                   include_docs=True, key=questionnaire.id)
        for media_detail_row in media_details_docs:
            dbm.database.delete(media_detail_row['doc'])


@login_required
@session_not_expired
@is_project_exist
@is_datasender_allowed
@project_has_web_device
@is_not_expired
@restrict_access
def new_xform_submission_get(request, project_id):
    survey_request = SurveyWebXformQuestionnaireRequest(request, project_id, XFormSubmissionProcessor())
    if request.method == 'GET':
        return survey_request.response_for_get_request()


@csrf_exempt
def new_xform_submission_post(request):
    try:
        response = XFormWebSubmissionHandler(request=request).create_new_submission_response()
        response['Location'] = request.build_absolute_uri(request.path)
        return response
    except ExceedSubmissionLimitException as e:
        return HttpResponse(json.dumps({'error_message': e.message}))
    except Exception as e:
        logger.exception("Exception in submission : \n%s" % e)
        send_email_on_exception(request.user, "New Web Submission", traceback.format_exc(),
                                additional_details={'submitted-data': request.POST['form_data']})
        return HttpResponseBadRequest()


@csrf_exempt
def edit_xform_submission_post(request, survey_response_id):
    manager = get_database_manager(request.user)
    survey_response = get_survey_response_by_id(manager, survey_response_id)
    activity_log = UserActivityLog()
    questionnaire = Project.get(manager, survey_response.form_model_id)
    old_data = survey_response._doc.values

    try:
        response = XFormWebSubmissionHandler(request=request). \
            update_submission_response(survey_response_id)
        new_survey_response = get_survey_response_by_id(manager, survey_response_id)
        new_data = new_survey_response._doc.values

        edit_details = _details_for_activity_log(new_data, old_data, questionnaire)
        activity_log.log(request, action=EDITED_DATA_SUBMISSION_ADV_QUEST, project=questionnaire.name,
                         detail=json.dumps(edit_details))

        return response

    except Exception as e:
        logger.exception("Exception in submission : \n%s" % e)
        send_email_on_exception(request.user, "Edit Web Submission", traceback.format_exc(),
                                additional_details={'survey_response_id': survey_response_id,
                                                    'submitted-data': request.POST['form_data']
                                                    })
        return HttpResponseBadRequest()


def _create_details_for_edit_info(new_data_dict, old_data, questionnaire):
    details = dict()
    for key, value in new_data_dict.iteritems():
        if key in old_data and key not in {"intro", "meta", "form_code"}:
            if new_data_dict[key] != old_data[key]:
                details.update({key: {'old_data': old_data[key], 'new_data': new_data_dict[key]}})
    return details


def _details_for_activity_log(new_data_dict, old_data, questionnaire):
    try:
        details = _create_details_for_edit_info(new_data_dict, old_data, questionnaire)
        edit_details = dict()
        for key, value in details.iteritems():
            question = questionnaire.get_field_by_code(key).label

            if isinstance(value['new_data'], list):   # for both group and repeat
                get_edited_details_for_field_set(edit_details, questionnaire, key, value)

            else:
                edit_details.update({key: {'question': question, 'old': old_data[key], 'new': new_data_dict[key]}})
        return edit_details
    except Exception as e:
        logger.exception("Exception in preparing activity log details : \n%s" % e)
        return ()


def get_edited_details_for_field_set(edit_details, questionnaire, code, value):
    if len(value['new_data']) == len(value['old_data']):
        if value['old_data'] != value['new_data']:
            for index, i in enumerate(value['new_data']):
                for key, val in i.items():
                    if key in value['old_data'][index]:
                        if val and val != value['old_data'][index][key]:
                            question = questionnaire.get_field_by_code(key).label
                            edit_details.update({code+'_'+str(index)+'_'+key: {'question': question,
                                                                               'old': value['old_data'][index][key], 'new': val}})

    if len(value['new_data']) > len(value['old_data']):
        for index, i in enumerate(value['new_data']):
            for key, val in i.items():
                try:
                    if key in value['old_data'][index]:
                        if val and val != value['old_data'][index][key]:
                            question = questionnaire.get_field_by_code(key).label
                            edit_details.update({code+'_'+str(index)+'_'+key: {'question': question,
                                                         'old': value['old_data'][index][key], 'new': val}})
                except IndexError:
                    if questionnaire.get_field_by_code(key):
                        question = questionnaire.get_field_by_code(key).label
                        edit_details.update({code+'_'+str(index)+'_'+key: {'question': question, 'old': "", 'new': val}})

    if len(value['new_data']) < len(value['old_data']):
        for index, i in enumerate(value['old_data']):
            for key, val in i.items():
                try:
                    if key in value['new_data'][index]:
                        if val != value['new_data'][index][key]:
                            question = questionnaire.get_field_by_code(key).label
                            edit_details.update({code+'_'+str(index)+'_'+key: {'question': question, 'old': val,
                                                         'new': value['new_data'][index][key]}})
                except IndexError:
                    if questionnaire.get_field_by_code(key):
                        question = questionnaire.get_field_by_code(key).label
                        edit_details.update({code+'_'+str(index)+'_'+key: {'question': question, 'old': val, 'new': ""}})


@login_required
@session_not_expired
@is_datasender
@is_not_expired
def project_download(request):
    project_name = request.POST.get(u"project_name")
    questionnaire_code = request.POST.get(u'questionnaire_code')

    manager = get_database_manager(request.user)
    questionnaire = get_form_model_by_code(manager, questionnaire_code)
    project = Project.from_form_model(questionnaire)
    try:
        raw_excel, file_extension = project.has_questionnaire_attachment()[1:]

        response = HttpResponse(mimetype="application/vnd.ms-excel", content=raw_excel)
        response['Content-Disposition'] = 'attachment; filename="%s.%s"' % (slugify(project_name), file_extension)

    except LookupError:
        response = HttpResponse(status=404)

    return response


@login_required
@session_not_expired
@is_datasender
@is_not_expired
def get_attachment(request, document_id, attachment_name):
    manager = get_database_manager(request.user)
    return HttpResponse(manager.get_attachments(document_id, attachment_name=attachment_name))


def send_email_if_unique_id_type_question_has_no_registered_unique_ids(xls_parser_response, request,
                                                                       questionnaire_name):
    if not xls_parser_response.info:
        # no info present
        return

    if xls_parser_response.info and ugettext('Making submission via Smartphone will not be possible.') not in \
            list(xls_parser_response.info)[0]:
        # 'unique id number not preset' info not present
        return

    send_no_registered_unique_id_for_type_email(request.user, questionnaire_name)


@login_required
@session_not_expired
@is_datasender
@is_not_expired
def attachment_download(request, document_id, attachment_name):
    manager = get_database_manager(request.user)
    try:
        raw_file = manager.get_attachments(document_id, attachment_name=attachment_name)
        mime_type = mimetypes.guess_type(os.path.basename(attachment_name))[0]
        response = HttpResponse(raw_file, mimetype=mime_type)
        file_extension = attachment_name.split('.')[-1]
        file_name_slugifed = "%s.%s" % (slugify(".".join(attachment_name.split('.')[:-1])), file_extension)
        response['Content-Disposition'] = 'attachment; filename="%s"' % file_name_slugifed
        return response
    except LookupError:
        return HttpResponse(status=404)


def send_email_on_exception(user, error_type, stack_trace, additional_details=None):
    email_message = ''
    profile = user.get_profile()
    organization = Organization.objects.get(org_id=profile.org_id)
    file_contents = additional_details.pop('file_contents') if additional_details and additional_details.get(
        'file_contents') else None
    submitted_data = additional_details.pop('submitted-data') if additional_details and additional_details.get(
        'submitted-data') else None
    email_message += '\nError Scenario : %s (%s)\n' % (organization.name, profile.org_id)
    email_message += '\nOrganization Details : %s (%s)' % (organization.name, profile.org_id)
    email_message += '\nUser Email Id : %s\n' % user.username
    email_message += '\n%s' % stack_trace
    email = EmailMessage(subject="[ERROR] %s : %s" % (error_type, organization.name),
                         body=repr(re.sub("\n", "<br/>", email_message)),
                         from_email=EMAIL_HOST_USER, to=[HNI_SUPPORT_EMAIL_ID])
    email.content_subtype = "html"

    if file_contents:
        email.attach("errored_excel.xls", content=file_contents, mimetype='application/ms-excel')

    if submitted_data:
        email.attach("submitted-data.xml", content=submitted_data, mimetype='text/xml')

    email.send()


def send_no_registered_unique_id_for_type_email(user, questionnaire_name):
    email_message = "Questionnaire '%s' created by '%s' has one or more unique-id question(s) with no registered unique-ids." % (
        questionnaire_name, user.email)
    profile = user.get_profile()
    organization = Organization.objects.get(org_id=profile.org_id)
    email = EmailMessage(subject="[INFO - No regd. unique-ids] : %s" % organization.name,
                         body=re.sub("\n", "<br/>", email_message),
                         from_email=EMAIL_HOST_USER, to=[HNI_SUPPORT_EMAIL_ID])
    email.content_subtype = "html"

    email.send()


EXCEL_UPLOAD_FILE_SIZE = 10485760  # 10MB


def _perform_file_validations(request):
    errors = []
    if request.GET and request.GET.get("qqfile"):
        file_extension = _file_extension(request)
        if file_extension not in [".xls", ".xlsx"]:
            errors.append(_("Please upload an excel file"))
            return errors, file_extension

    if request.META.get('CONTENT_LENGTH') and int(request.META.get('CONTENT_LENGTH')) > EXCEL_UPLOAD_FILE_SIZE:
        errors.append(_("larger files than 10MB."))

    if errors:
        logger.info("User: %s. Edit upload File validation failed: %s. File name: %s, size: %d",
                    request.user.username,
                    json.dumps(errors), request.GET.get("qqfile"), int(request.META.get('CONTENT_LENGTH')))

        return HttpResponse(content_type='application/json', content=json.dumps({
            'success': False,
            'error_msg': errors
        }))


def _file_extension(request):
    return os.path.splitext(request.GET["qqfile"])[1]


def _temp_file(request, input_stream=None, file_type=None):
    tmp_file = None
    if request.GET and request.GET.get('qqfile'):
        tmp_file = NamedTemporaryFile(delete=True, suffix=_file_extension(request))
        tmp_file.write(request.raw_post_data)
        tmp_file.seek(0)
    if input_stream:
        tmp_file = NamedTemporaryFile(delete=True, suffix='.' + file_type)
        tmp_file.write(input_stream.getvalue())
        tmp_file.seek(0)

    return tmp_file


def _try_parse_xls(manager, request, questionnaire_name, excel_file=None):
    try:
        file_validation_results = _perform_file_validations(request)
        if isinstance(file_validation_results, HttpResponse):
            return file_validation_results

        if excel_file:
            tmp_file = excel_file
        else:
            tmp_file = _temp_file(request)

        xls_parser_response = XlsFormParser(tmp_file, questionnaire_name, manager).parse()

        profile = request.user.get_profile()
        organization = Organization.objects.get(org_id=profile.org_id)
        if xls_parser_response.is_multiple_languages:
            logger.info("Edit Questionnaire %s with Multi Language support for organization : %s(%s) and email: %s",
                        questionnaire_name, organization.name, profile.org_id, profile.user.email)

        if xls_parser_response.errors:
            info_list = list(xls_parser_response.errors)
            logger.info("User: %s. Edit upload Errors: %s", request.user.username, json.dumps(info_list))

            return HttpResponse(content_type='application/json', content=json.dumps({
                'success': False,
                'status': 'error',
                'error_msg': info_list,
                'message_prefix': _("Sorry! Current version of DataWinners does not support"),
                'message_suffix': _("Update your XLSForm and upload again.")
            }))

    except BindError as e:
        error_message = [(e.custom_message + ' for ' + e.attribute + ' in the question ' + e.field.name)]
        return HttpResponse(content_type='application/json', content=json.dumps({
            'success': False,
            'status': 'error',
            'error_msg': error_message,
            'errors': [
                {
                    e.field.name: {
                        e.attribute: e.custom_message
                    }
                }
            ],
            'reason': 'Save Failed',  # TODO: i18n translation
            'details': ''
        }))

    except PyXFormError as e:
        logger.info("User: %s. Upload Error: %s", request.user.username, e.message)

        message = transform_error_message(e.message)
        if 'name_type_error' in message or 'choice_name_type_error' in message:
            if 'choice_name_type_error' in message:
                message_prefix = _(
                    "On your \"choices\" sheet the first and second column must be \"list_name\" and \"name\".  Possible errors:")
            else:
                message_prefix = _(
                    "On your \"survey\" sheet the first and second column must be \"type\" and \"name\".  Possible errors:")
            return HttpResponse(content_type='application/json', content=json.dumps({
                'success': False,
                'status': 'error',
                'details': message,
                'reason': 'Save Failed',
                'error_msg': [_("Columns are missing"), _("Column name is misspelled"),
                              _("Additional space in column name")],
                'message_prefix': message_prefix,
                'message_suffix': _("Update your XLSForm and upload again.")
            }))
        else:
            return HttpResponse(content_type='application/json', content=json.dumps({
                'success': False,
                'status': 'error',
                'details': message,
                'reason': 'Save Failed',
                'error_msg': [message if message else ugettext(
                    "all XLSForm features. Please check the list of unsupported features.")]
            }))

    except UnicodeDecodeError as e:
        logger.info("User: %s. Upload Error: %s", request.user.username, e.message)

        return HttpResponse(content_type='application/json', content=json.dumps({
            'success': False,
            'status': 'error',
            'details': e.message,
            'reason': 'Save Failed',
            'error_msg': [
                _(
                    "Check your columns for errors.<br>There are missing symbols (like $ for relevant or calculate) or incorrect characters<br>") + _(
                    "Update your XLSForm and upload again.")],
        }))

    except Exception as e:

        logger.info("User: %s. Edit Upload Exception message: %s", request.user.username, e.message)

        message = e.message if e.message else _("Some error in excel")
        odk_message = ''
        if not 'ODK Validate Errors:' in e.message:
            send_email_on_exception(request.user, "Questionnaire Edit", traceback.format_exc(),
                                    additional_details={'file_contents': request.raw_post_data})
        else:
            odk_message = translate_odk_message(e.message)
        message = odk_message if odk_message else message
        return HttpResponse(content_type='application/json', content=json.dumps({
            'error_msg': [message], 'success': False, 'status': 'error', 'details': message,
            'reason': 'Save Failed'
        }))

    return xls_parser_response


@login_required
@session_not_expired
@is_project_exist
@is_not_expired
def external_itemset(request, questionnaire_code=None):
    request_user = request.user
    return itemset_for(get_database_manager(request_user), questionnaire_code)


class SurveyWebXformQuestionnaireRequest(SurveyWebQuestionnaireRequest):
    def __init__(self, request, project_id=None, submissionProcessor=None):
        SurveyWebQuestionnaireRequest.__init__(self, request, project_id)
        self.submissionProcessor = submissionProcessor
        self.is_data_sender = request.user.get_profile().reporter

    @property
    def template(self):
        return 'project/xform_web_questionnaire_datasender.html' if self.is_data_sender \
            else 'project/xform_web_questionnaire.html'

    def response_for_get_request(self, initial_data=None, is_update=False):
        dashboard_page = settings.HOME_PAGE + "?deleted=true"
        if self.questionnaire.is_void():
            return HttpResponseRedirect(dashboard_page)
        questionnaire_form = self.form(initial_data=initial_data)
        form_context = get_form_context(self.questionnaire, questionnaire_form,
                                        self.manager, self.hide_link_class, self.disable_link_class,
                                        is_update=is_update)
        if self.questionnaire.xform:
            form_context.update(
                {
                    'xform_xml': re.sub(r"\n", " ", XFormTransformer(
                        self.questionnaire.do_enrich_xform()).transform())
                })
            form_context.update({'is_advance_questionnaire': True})
            form_context.update({'external_itemset_url': self._get_itemset_url(self.questionnaire)})
            form_context.update({'submission_create_url': reverse('new_web_submission')})
        form_context.update({'is_quota_reached': is_quota_reached(self.request)})
        return render_to_response(self.template, form_context, context_instance=RequestContext(self.request))

    def _model_str_of(self, survey_response_id, project_name):
        # TODO this can be avoided
        survey_response = get_survey_response_by_id(self.manager, survey_response_id)
        xform_instance_xml = self.submissionProcessor. \
            get_model_edit_str(self.questionnaire.fields, survey_response.values, project_name,
                               self.questionnaire.form_code, )
        return xform_instance_xml

    def response_for_xform_edit_get_request(self, survey_response_id):

        # todo delete/refactor this block
        dashboard_page = settings.HOME_PAGE + "?deleted=true"
        if self.questionnaire.is_void():
            return HttpResponseRedirect(dashboard_page)
        questionnaire_form = self.form(initial_data=None)
        form_context = get_form_context(self.questionnaire, questionnaire_form,
                                        self.manager, self.hide_link_class, self.disable_link_class, is_update=False)

        if self.questionnaire.xform:
            form_context.update({'survey_response_id': survey_response_id})
            # xform_transformer = XFormTransformer(self.questionnaire.xform)
            form_context.update({'xform_xml': re.sub(r"\n", " ", XFormTransformer(
                self.questionnaire.do_enrich_xform()).transform())})
            form_context.update(
                {'edit_model_str': self._model_str_of(survey_response_id,
                                                      get_generated_xform_id_name(self.questionnaire.xform))})
            form_context.update({'external_itemset_url': self._get_itemset_url(self.questionnaire)})
            form_context.update({'submission_update_url': reverse('update_web_submission',
                                                                  kwargs={'survey_response_id': survey_response_id})})
            form_context.update({'is_advance_questionnaire': True})

        form_context.update({'is_quota_reached': is_quota_reached(self.request)})
        return render_to_response(self.template, form_context, context_instance=RequestContext(self.request))

    def _to_html(self, data):
        html = '<ul>'
        for key, value in data.items():
            if isinstance(value, dict):
                continue
            html += '<li>' + key + ' : '
            if isinstance(value, list):
                for v in value:
                    html += self._to_html(v)
            else:
                html += value if value else ""
            html += '</li>'

        html += '</ul>'
        return html

    def get_submissions(self):
        submission_list = []
        submissions = get_survey_responses(self.manager, self.questionnaire.id, None, None,
                                           view_name="undeleted_survey_response")
        for submission in submissions:
            submission_list.append({'submission_uuid': submission.id,
                                    'version': submission.version,
                                    'project_uuid': self.questionnaire.id,
                                    'created': py_datetime_to_js_datestring(submission.created)
                                    })
        return submission_list

    def get_submission(self, submission_uuid):
        submission = get_survey_response_by_id(self.manager, submission_uuid)
        imageProcessor = XFormImageProcessor()

        return {'submission_uuid': submission.id,
                'version': submission.version,
                'project_uuid': self.questionnaire.id,
                'created': py_datetime_to_js_datestring(submission.created),
                'media_file_names_string': imageProcessor
                    .get_media_files_str(self.questionnaire.fields, submission.values),
                'xml': self._model_str_of(submission.id, get_generated_xform_id_name(self.questionnaire.xform)),
                'data': json.dumps(submission.values)
                }

    def _get_itemset_url(self, questionnaire):
        xform_base_url = self.request.build_absolute_uri('/xlsform')
        return xform_base_url + '/itemset/' + questionnaire.id