Пример #1
0
def main_form_splitter(request, project_slug='soiltrack'):
    ''' Unified EthioSIS_ET Form to per-sample (ESTS_ET) ones (submits to FH)

        1. Receives a JSON POST from formhub containing qr_* barcodes
        2. For each level (qr), prepare a step-specific submission
        3. Submits the resulting XForms to formhub. '''

    # we need a project to guess formhub URL
    try:
        project = Project.objects.get(slug=project_slug)
    except:
        project = Project.objects.all()[0]

    try:
        jsform = json.loads(request.raw_post_data)
        # re-organize JSON to match semantic
        nest_flat_dict(jsform)
    except:
        return HttpResponse(u"Unable to parse JSON data", status=400)

    positions = {
        'top_qr': None,
        'sub_qr': None,
        'qr_0_20': None,
        'qr_20_40': None,
        'qr_40_60': None,
        'qr_60_80': None,
        'qr_80_100': None,
    }

    forms = []
    found = bool(len(jsform.get(u'found', {})) > 1)

    for key in positions.keys():
        positions[key] = jsform.get(u'found', {}).get(key, '')
        if not re.match(r'[a-zA-Z0-9\_\-]+', positions[key]):
            positions[key] = None

    # delete all position keys
    for key in positions.keys():
        try:
            jsform[u'found'].pop(key)
        except KeyError:
            pass

    for position in positions.keys():
        if not positions[position]:
            continue

        # extract sample identifier
        barcode = positions.get(position)

        # duplicate whole form to grab meta data.
        form = copy.deepcopy(jsform)

        # add `barcode` field
        form[u'barcode'] = barcode
        form[u'position'] = position

        forms.append(form)

    if not found:
        # make a single submission since we don't have barcodes
        form = copy.deepcopy(jsform)
        form[u'barcode'] = u'n/a__%s' % uuid.uuid4().hex
        form[u'position'] = u'not_found'
        forms.append(form)

    # free some mem
    del(jsform)

    def json2xform(jsform):
        # changing the form_id to match correct Step
        dd = {'form_id': u'ESTS_ET_sample'}
        xml_head = u"<?xml version='1.0' ?><%(form_id)s id='%(form_id)s'>" % dd
        xml_tail = u"</%(form_id)s>" % dd

        # remove the parent's instance ID and step
        try:
            jsform[u'meta'].pop('instanceID')
        except KeyError:
            pass

        for field in jsform.keys():
            # treat field starting with underscore are internal ones.
            # and remove them
            if field.startswith('_'):
                jsform.pop(field)

        return xml_head + dict2xml(jsform) + xml_tail

    xforms = [json2xform(form) for form in forms]

    try:
        submit_xml_forms_formhub(project, xforms, as_bulk=True)
    except (ErrorUploadingDataToFormhub,
            ErrorMultipleUploadingDataToFormhub) as e:
        return HttpResponse(u"%(intro)s\n%(detail)s"
                            % {'intro': e,
                               'detail': e.details()}, status=502)
    except Exception as e:
        return HttpResponse(str(e), status=500)

    return HttpResponse('OK', status=201)
Пример #2
0
def form_splitter(request, project_slug='soildoc'):
    ''' Master XForm to Sub XFrom

        1. Receives a grouped JSON POST from formhub containing A-Z sample data
        2. Extract and transform data for each sample into a new XForm
        3. Submits the resulting XForms to formhub. '''

    # we need a project to guess formhub URL
    try:
        project = Project.objects.get(slug=project_slug)
    except:
        project = Project.objects.all()[0]

    try:
        jsform = json.loads(request.raw_post_data)
    except:
        return HttpResponse(u"Unable to parse JSON data", status=400)

    def field_splitter(field):
        match = re.match(r'.*_([a-i])$', field)
        if match:
            try:
                suffix = match.groups()[0]
            except:
                suffix = None
            if suffix:
                field = field.rsplit('_%s' % suffix, 1)[0]
            return (field, suffix)
        else:
            return (field, None)

    # name of a field which if None marks the form as empty
    # we don't submit empty forms to formhub.
    # must be a suffixed field!
    AVAIL_SUFFIXES = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
    # empty_trigger = 'sample_id_$$/sample_manual_id_$$'

    # map field suffixes with IDs in holder
    # we exclude forms with no data on trigger field so it won't be process
    # nor sent to formhub
    indexes = [l for l in AVAIL_SUFFIXES
                 if (jsform.get('sample_id_$$/sample_barcode_id_$$'.replace('$$', l), None)
                     or jsform.get('sample_id_$$/sample_manual_id_$$'.replace('$$', l), None))]

    # initialize holder for each form]
    forms = [{'single_letter': l} for l in indexes]

    for field, value in jsform.iteritems():
        # if fields ends in a-h, only add it to the specified form
        target_field, target_suffix = field_splitter(field)

        if target_suffix in indexes:
            # retrieve suffix, form and build target field (without suffix)
            form = forms[indexes.index(target_suffix)]

            # handle group field differently (parent holding the fields)
            if '/' in target_field:
                group, real_field = target_field.split('/', 1)
                real_group, group_suffix = field_splitter(group)
                if not real_group in form:
                    form[real_group] = {}
                form[real_group].update({real_field: value})
            else:
                form.update({target_field: value})
        # otherwise, it's a common field, add to all
        else:
            for form in forms:
                # handle group field differently (parent holding the fields)
                if '/' in target_field:
                    group, real_field = target_field.split('/', 1)
                    real_group, group_suffix = field_splitter(group)
                    if not real_group in form:
                        form[real_group] = {}
                    form[real_group].update({real_field: value})
                else:
                    form.update({target_field: value})

    del(jsform)

    # we now have a list of json forms each containing their data.
    def json2xform(jsform):
        # changing the form_id to XXX_single
        dd = {'form_id': u'%s_single' % jsform.get(u'_xform_id_string')}
        xml_head = u"<?xml version='1.0' ?><%(form_id)s id='%(form_id)s'>" % dd
        xml_tail = u"</%(form_id)s>" % dd

        # remove the parent's instance ID
        try:
            jsform['meta'].pop('instanceID')
        except KeyError:
            pass

        for field in jsform.keys():
            # treat field starting with underscore are internal ones.
            # and remove them
            if field.startswith('_'):
                jsform.pop(field)

        return xml_head + dict2xml(jsform) + xml_tail

    xforms = [json2xform(forms[indexes.index(i)].copy()) for i in indexes]

    try:
        submit_xml_forms_formhub(project, xforms, as_bulk=False)
    except (ErrorUploadingDataToFormhub,
            ErrorMultipleUploadingDataToFormhub) as e:
        return HttpResponse(u"%(intro)s\n%(detail)s"
                            % {'intro': e,
                               'detail': e.details()}, status=502)
    except Exception as e:
        return HttpResponse(str(e), status=500)

    return HttpResponse('OK', status=201)
Пример #3
0
def steps_form_splitter(request, project_slug='soiltrack'):
    ''' Unified (ESTS_Steps) Form to individual ones (submits to FH)

        1. Receives a JSON POST from formhub containing x samples barcode
        2. For each sample, prepare a step-specific submission
        3. Submits the resulting XForms to formhub. '''

    # we need a project to guess formhub URL
    try:
        project = Project.objects.get(slug=project_slug)
    except:
        project = Project.objects.all()[0]

    try:
        jsform = json.loads(request.raw_post_data)
        # re-organize JSON to match semantic
        nest_flat_dict(jsform)
    except:
        return HttpResponse(u"Unable to parse JSON data", status=400)

    # we have a json dict containing all fields.
    # we need to:
    #   1. explode the form into x ones from soil_id
    #   2. find out step
    #   2. rename all fields except barcode to prefix with step

    forms = []
    step = jsform[u'step']

    for soil_field in jsform[u'scan']:
        # looping on repeat field `scan` which contains `soil_id`
        barcode = soil_field[u'scan/soil_id']

        # duplicate whole form to grab meta data.
        form = copy.deepcopy(jsform)

        # rename all fields but `_` starting ones
        keys = form.keys()
        for key in keys:
            # _ starting keys are internal.
            if key.startswith('_'):
                continue
            form.update({'%s_%s' % (step, key): form[key]})
            # delete key once duplicated
            form.pop(key)
        # remove repeat section and replace with `barcode`
        form.pop(u'%s_scan' % step)
        form.update({u'barcode': barcode})

        forms.append(form)
    del(jsform)

    def json2xform(jsform):
        # changing the form_id to match correct Step
        dd = {'form_id': u'ESTS_%s' % step.title()}
        xml_head = u"<?xml version='1.0' ?><%(form_id)s id='%(form_id)s'>" % dd
        xml_tail = u"</%(form_id)s>" % dd

        # remove the parent's instance ID and step
        try:
            jsform['%s_meta' % step].pop('instanceID')
            # jsform['%s_formhub' % step].pop('uuid')
            jsform.pop('%s_step' % step)
        except KeyError:
            pass

        for field in jsform.keys():
            # treat field starting with underscore are internal ones.
            # and remove them
            if field.startswith('_'):
                jsform.pop(field)

        return xml_head + dict2xml(jsform) + xml_tail

    xforms = [json2xform(form) for form in forms]

    try:
        submit_xml_forms_formhub(project, xforms, as_bulk=True)
    except (ErrorUploadingDataToFormhub,
            ErrorMultipleUploadingDataToFormhub) as e:
        return HttpResponse(u"%(intro)s\n%(detail)s"
                            % {'intro': e,
                               'detail': e.details()}, status=502)
    except Exception as e:
        return HttpResponse(str(e), status=500)

    return HttpResponse('OK', status=201)