Exemplo n.º 1
0
    def dispatch_with_args(self, body, argMap):
        'Perform callback dispatching, with enhanced arguments and recording of response'
        inputs = body.get('inputs', [])
        input_values = inputs_to_dict(inputs)
        states = body.get('state', [])
        output = body['output']
        outputs_list = body.get('outputs') or split_callback_id(output)
        changed_props = body.get('changedPropIds', [])
        triggered_inputs = [{
            "prop_id": x,
            "value": input_values.get(x)
        } for x in changed_props]

        callback_context_info = {
            'inputs_list': inputs,
            'inputs': input_values,
            'states_list': states,
            'states': inputs_to_dict(states),
            'outputs_list': outputs_list,
            'outputs': outputs_list,
            'triggered': triggered_inputs,
        }

        callback_context = CallbackContext(**callback_context_info)

        # Overload dash global variable
        dash.callback_context = callback_context

        # Add context to arg map, if extended callbacks in use
        if len(argMap) > 0:
            argMap['callback_context'] = callback_context

        single_case = not (output.startswith('..') and output.endswith('..'))
        if single_case:
            # single Output (not in a list)
            outputs = [output]
        else:
            # multiple outputs in a list (the list could contain a single item)
            outputs = output[2:-2].split('...')

        da = argMap.get('dash_app', None)

        callback_info = self.callback_map[output]

        args = []

        for c in inputs + states:
            if isinstance(c, list):  # ALL, ALLSMALLER
                v = [ci.get("value") for ci in c]
                if da:
                    for ci, vi in zip(c, v):
                        da.update_current_state(ci['id'], ci['property'], vi)
            else:
                v = c.get("value")
                if da:
                    da.update_current_state(c['id'], c['property'], v)

            args.append(v)

        # Dash 1.11 introduces a set of outputs
        outputs_list = body.get('outputs') or split_callback_id(output)
        argMap['outputs_list'] = outputs_list

        # Special: intercept case of insufficient arguments
        # This happens when a property has been updated with a pipe component
        # TODO see if this can be attacked from the client end

        if len(args) < len(callback_info['inputs']):
            return 'EDGECASEEXIT'

        callback = callback_info["callback"]
        # smart injection of parameters if .expanded is defined
        if callback.expanded is not None:
            parameters_to_inject = {*callback.expanded, 'outputs_list'}
            res = callback(
                *args, **{
                    k: v
                    for k, v in argMap.items() if k in parameters_to_inject
                })
        else:
            res = callback(*args, **argMap)

        if da:

            class LazyJson:
                """A class to allow delayed the evaluation of a dict (returned by `func`)
                 till the first get(...) is called on the dict."""
                def __init__(self, func):
                    self._root_value = func

                def get(self, item, default):
                    if isinstance(self._root_value, Callable):
                        self._root_value = self._root_value()
                    return self._root_value.get(item, default)

            # wraps the json parsing of the response into LazyJson to avoid unnecessary parsing
            root_value = LazyJson(lambda: json.loads(res).get('response', {}))

            for output_item in outputs:
                if isinstance(output_item, str):
                    output_id, output_property = output_item.split('.')
                    if da.have_current_state_entry(output_id, output_property):
                        value = root_value.get(output_id,
                                               {}).get(output_property, None)
                        da.update_current_state(output_id, output_property,
                                                value)
                else:
                    # todo: implement saving of state for pattern matching ouputs
                    raise NotImplementedError(
                        "Updating state for dict keys (pattern matching) is not yet implemented"
                    )

        return res
Exemplo n.º 2
0
    def dispatch_with_args(self, body, argMap):
        'Perform callback dispatching, with enhanced arguments and recording of response'
        inputs = body.get('inputs', [])
        state = body.get('state', [])
        output = body['output']

        outputs = []
        try:
            if output[:2] == '..' and output[-2:] == '..':
                # Multiple outputs
                outputs = output[2:-2].split('...')
                target_id = output
                # Special case of a single output
                if len(outputs) == 1:
                    target_id = output[2:-2]
        except:
            pass

        single_case = False
        if len(outputs) < 1:
            try:
                output_id = output['id']
                output_property = output['property']
                target_id = "%s.%s" %(output_id, output_property)
            except:
                target_id = output
                output_id, output_property = output.split(".")
            single_case = True
            outputs = [output,]

        args = []
        da = argMap.get('dash_app', None)
        for component_registration in self.callback_map[target_id]['inputs']:
            if isinstance(component_registration , list):
                continue
            for c in inputs:
                if isinstance(c, list):
                    continue
                if c['property'] == component_registration['property'] and c['id'] == component_registration['id']:
                    v = c.get('value', None)
                    args.append(v)
                    if da:
                        da.update_current_state(c['id'], c['property'], v)

        for component_registration in self.callback_map[target_id]['inputs']:
            try:
                iddict = eval(component_registration['id'])
                if not isinstance(iddict, dict):
                    continue
            except NameError:
                continue
            for c in inputs:
                # print(c, component_registration, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
                if not isinstance(c, list):
                    continue
                if iddict['index'][0]=='ALL':
                    selectlist = [i for i in c if i['id']['type'] ==iddict['type'] and i['property']==component_registration['property']]
                    v = [sel.get('value', None) for sel in selectlist]
                    args.append(v)
                    if da:
                        for sel in selectlist:
                            da.update_current_state(sel['id'], sel['property'], v)

                else:
                    raise NotImplementedError

        for component_registration in self.callback_map[target_id]['state']:
            for c in state:
                if c['property'] == component_registration['property'] and c['id'] == component_registration['id']:
                    v = c.get('value', None)
                    args.append(v)
                    if da:
                        da.update_current_state(c['id'], c['property'], v)

        # Special: intercept case of insufficient arguments
        # This happens when a propery has been updated with a pipe component
        # TODO see if this can be attacked from the client end

        if len(args) < len(self.callback_map[target_id]['inputs']):
            return 'EDGECASEEXIT'

        outputs_list = body.get('outputs') or split_callback_id(output)
        if  'container' in output:
            argMap = {'outputs_list': outputs_list
                      }
        else:
            argMap['outputs_list'] = outputs_list
        # del argMap['dash_app_id']
        # del argMap['dash_app']

        # print(args, argMap)
        # (1, []) {'dash_app_id': 'BootstrapApplication', 'dash_app': <django_plotly_dash.dash_wrapper.DjangoDash object at 0x7f794ae5cc50>, 'user': <SimpleLazyObject: <function AuthenticationMiddleware.process_request.<locals>.<lambda> at 0x7f79498c2b00>>, 'request': <AsgiRequest: POST '/django_plotly_dash/app/BootstrapApplication/_dash-update-component'>,
        # 'session_state': {'calls_so_far': 12, 'user_counts': {'AnonymousUser': 12}, 'ind_use': 1, 'bootstrap_demo_state': {'clicks': 0, 'overall': 0}}, 'outputs_list': {'id': 'dropdown-container', 'property': 'children'}}

        res = self.callback_map[target_id]['callback'](*args, **argMap)
        if da:
            if single_case and da.have_current_state_entry(output_id, output_property):
                response = json.loads(res.data.decode('utf-8'))
                value = response.get('response', {}).get('props', {}).get(output_property, None)
                da.update_current_state(output_id, output_property, value)

            response = json.loads(res)
            root_value = response.get('response', {})
            for output_item in outputs:
                if isinstance(output_item, str):
                    output_id, output_property = output_item.split('.')
                    if da.have_current_state_entry(output_id, output_property):
                        value = root_value.get(output_id,{}).get(output_property, None)
                        da.update_current_state(output_id, output_property, value)

        return res
Exemplo n.º 3
0
    def dispatch_with_args(self, body, argMap):
        'Perform callback dispatching, with enhanced arguments and recording of response'
        inputs = body.get('inputs', [])
        input_values = inputs_to_dict(inputs)
        states = body.get('state', [])
        output = body['output']
        outputs_list = body.get('outputs') or split_callback_id(output)
        changed_props = body.get('changedPropIds', [])
        triggered_inputs = [{
            "prop_id": x,
            "value": input_values.get(x)
        } for x in changed_props]

        callback_context_info = {
            'inputs_list': inputs,
            'inputs': input_values,
            'states_list': states,
            'states': inputs_to_dict(states),
            'outputs_list': outputs_list,
            'outputs': outputs_list,
            'triggered': triggered_inputs,
        }

        callback_context = CallbackContext(**callback_context_info)

        # Overload dash global variable
        dash.callback_context = callback_context

        # Add context to arg map, if extended callbacks in use
        if len(argMap) > 0:
            argMap['callback_context'] = callback_context

        outputs = []
        try:
            if output[:2] == '..' and output[-2:] == '..':
                # Multiple outputs
                outputs = output[2:-2].split('...')
                target_id = output
                # Special case of a single output
                if len(outputs) == 1:
                    target_id = output[2:-2]
        except:
            pass

        single_case = False
        if len(outputs) < 1:
            try:
                output_id = output['id']
                output_property = output['property']
                target_id = "%s.%s" % (output_id, output_property)
            except:
                target_id = output
                output_id, output_property = output.split(".")
            single_case = True
            outputs = [
                output,
            ]

        args = []

        da = argMap.get('dash_app', None)

        for component_registration in self.callback_map[target_id]['inputs']:
            for c in inputs:
                if c['property'] == component_registration['property'] and c[
                        'id'] == component_registration['id']:
                    v = c.get('value', None)
                    args.append(v)
                    if da:
                        da.update_current_state(c['id'], c['property'], v)

        for component_registration in self.callback_map[target_id]['state']:
            for c in states:
                if c['property'] == component_registration['property'] and c[
                        'id'] == component_registration['id']:
                    v = c.get('value', None)
                    args.append(v)
                    if da:
                        da.update_current_state(c['id'], c['property'], v)

        # Dash 1.11 introduces a set of outputs
        outputs_list = body.get('outputs') or split_callback_id(output)
        argMap['outputs_list'] = outputs_list

        # Special: intercept case of insufficient arguments
        # This happens when a propery has been updated with a pipe component
        # TODO see if this can be attacked from the client end

        if len(args) < len(self.callback_map[target_id]['inputs']):
            return 'EDGECASEEXIT'

        res = self.callback_map[target_id]['callback'](*args, **argMap)
        if da:
            if single_case and da.have_current_state_entry(
                    output_id, output_property):
                response = json.loads(res.data.decode('utf-8'))
                value = response.get('response',
                                     {}).get('props',
                                             {}).get(output_property, None)
                da.update_current_state(output_id, output_property, value)

            response = json.loads(res)
            root_value = response.get('response', {})
            for output_item in outputs:
                if isinstance(output_item, str):
                    output_id, output_property = output_item.split('.')
                    if da.have_current_state_entry(output_id, output_property):
                        value = root_value.get(output_id,
                                               {}).get(output_property, None)
                        da.update_current_state(output_id, output_property,
                                                value)

        return res
Exemplo n.º 4
0
    def dispatch_with_args(self, body, argMap):
        'Perform callback dispatching, with enhanced arguments and recording of response'
        inputs = body.get('inputs', [])
        input_values = inputs_to_dict(inputs)
        states = body.get('state', [])
        output = body['output']
        outputs_list = body.get('outputs') or split_callback_id(output)
        changed_props = body.get('changedPropIds', [])
        triggered_inputs = [{"prop_id": x, "value": input_values.get(x)} for x in changed_props]

        callback_context_info = {
            'inputs_list': inputs,
            'inputs': input_values,
            'states_list': states,
            'states': inputs_to_dict(states),
            'outputs_list': outputs_list,
            'outputs': outputs_list,
            'triggered': triggered_inputs,
            }

        callback_context = CallbackContext(**callback_context_info)

        # Overload dash global variable
        dash.callback_context = callback_context

        # Add context to arg map, if extended callbacks in use
        if len(argMap) > 0:
            argMap['callback_context'] = callback_context

        single_case = not(output.startswith('..') and output.endswith('..'))
        if single_case:
            # single Output (not in a list)
            outputs = [output]
        else:
            # multiple outputs in a list (the list could contain a single item)
            outputs = output[2:-2].split('...')

        args = []

        da = argMap.get('dash_app', None)

        callback_info = self.callback_map[output]

        for component_registration in callback_info['inputs']:
            for c in inputs:
                if c['property'] == component_registration['property'] and compare(id_python=c['id'],id_dash=component_registration['id']):
                    v = c.get('value', None)
                    args.append(v)
                    if da:
                        da.update_current_state(c['id'], c['property'], v)

        for component_registration in callback_info['state']:
            for c in states:
                if c['property'] == component_registration['property'] and compare(id_python=c['id'],id_dash=component_registration['id']):
                    v = c.get('value', None)
                    args.append(v)
                    if da:
                        da.update_current_state(c['id'], c['property'], v)

        # Dash 1.11 introduces a set of outputs
        outputs_list = body.get('outputs') or split_callback_id(output)
        argMap['outputs_list'] = outputs_list

        # Special: intercept case of insufficient arguments
        # This happens when a property has been updated with a pipe component
        # TODO see if this can be attacked from the client end

        if len(args) < len(callback_info['inputs']):
            return 'EDGECASEEXIT'

        callback = callback_info["callback"]
        # smart injection of parameters if .expanded is defined
        if callback.expanded is not None:
            parameters_to_inject = {*callback.expanded, 'outputs_list'}
            res = callback(*args, **{k: v for k, v in argMap.items() if k in parameters_to_inject})
        else:
            res = callback(*args, **argMap)

        if da:
            root_value = json.loads(res).get('response', {})

            for output_item in outputs:
                if isinstance(output_item, str):
                    output_id, output_property = output_item.split('.')
                    if da.have_current_state_entry(output_id, output_property):
                        value = root_value.get(output_id,{}).get(output_property, None)
                        da.update_current_state(output_id, output_property, value)
                else:
                    # todo: implement saving of state for pattern matching ouputs
                    raise NotImplementedError("Updating state for dict keys (pattern matching) is not yet implemented")

        return res