예제 #1
0
def create_sankey_graph_from_resources(resources):
    """
        Given Sankey data process it into Sankey graph data
    :param resources: Resource instances
    :return:
    """
    unindexed_graph = R.reduce(accumulate_sankey_graph, dict(nodes=[],
                                                             links=[]),
                               resources)
    return index_sankey_graph(unindexed_graph)
예제 #2
0
def enforce_unique_props(property_fields, django_instance_data):
    """
        Called in the mutate function of the Graphene Type class. Ensures that all properties marked
        as unique_with
    :param property_fields: The Graphene Type property fields dict. This is checked for unique_with,
    which when present points at a function that expects the django_instance_data and returns the django_instance_data
    modified so that the property in question has a unique value.
    :param django_instance_data: dict of an instance to be created or updated
    :param for_update if True this is for an update mutation so props are not required
    :return: The modified django_instance_data for any property that needs to have a unique value
    """

    # If any prop needs to be unique then run its unique_with function, which updates it to a unique value
    # By querying the database for duplicate. This is mainly for non-pk fields like a key
    return R.reduce(
        lambda reduced, prop_field_tup: prop_field_tup[1]['unique_with'](reduced) if
        R.has(prop_field_tup[0], reduced) and R.prop_or(False, 'unique_with', prop_field_tup[1]) else
        reduced,
        django_instance_data,
        property_fields.items()
    )
예제 #3
0
    def parse_literal(cls, node):
        """
            Parses any array string
        :param node:
        :return:
        """
        def map_value(value):
            """
                value is either a ListValue with values that are ListValues or a ListValue with values that
                are FloatValues
            :param value:
            :return:
            """
            def handle_floats(v):
                if hasattr(v, 'values'):
                    # Multiple floats:w
                    return R.map(lambda fv: float(fv.value), v.values)
                else:
                    # Single float
                    return float(v.value)

            return R.if_else(
                lambda v: R.isinstance(
                    ListValue,
                    R.head(v.values) if hasattr(v, 'values') else v),
                # ListValues
                lambda v: [reduce(v.values)],
                # FloatValues or single FloatValue
                lambda v: [handle_floats(v)])(value)

        def reduce(values):
            return R.reduce(
                lambda accum, list_values: R.concat(accum,
                                                    map_value(list_values)),
                [], values)

        # Create the coordinates by reducing node.values=[node.values=[node.floats], node.value, ...]
        return R.reduce(lambda accum, list_values: reduce(node.values), [],
                        reduce(node.values))
예제 #4
0
def generate_sankey_data(resource):
    """
        Generates nodes and links for the given Resrouce object
    :param resource:  Resource object
    :return: A dict containing nodes and links. nodes are a dict key by stage name
        Results can be assigned to resource.data.sankey and saved
    """

    settings = R.item_path(['data', 'settings'], resource)
    stages = R.prop('stages', settings)
    stage_key = R.prop('stage_key', settings)
    value_key = R.prop('value_key', settings)
    location_key = R.prop('location_key', settings)
    node_name_key = R.prop('node_name_key', settings)
    default_location = R.prop('default_location', settings)
    # A dct of stages by name
    stage_by_name = stages_by_name(stages)

    def accumulate_nodes(accum, raw_node, i):
        """
            Accumulate each node, keying by the name of the node's stage key
            Since nodes share stage keys these each result is an array of nodes
        :param accum:
        :param raw_node:
        :param i:
        :return:
        """
        location_obj = resolve_location(default_location,
                                        R.prop(location_key, raw_node), i)
        location = R.prop('location', location_obj)
        is_generalized = R.prop('is_generalized', location_obj)
        # The key where then node is stored is the stage key
        key = R.prop('key', stage_by_name[raw_node[stage_key]])

        # Copy all properties from resource.data  except settings and raw_data
        # Also grab raw_node properties
        # This is for arbitrary properties defined in the data
        # We put them in properties and property_values since graphql hates arbitrary key/values
        properties = R.merge(
            R.omit(['settings', 'raw_data'], R.prop('data', resource)),
            raw_node)
        return R.merge(
            # Omit accum[key] since we'll concat it with the new node
            R.omit([key], accum),
            {
                # concat accum[key] or [] with the new node
                key:
                R.concat(
                    R.prop_or([], key, accum),
                    # Note that the value is an array so we can combine nodes with the same stage key
                    [
                        dict(value=string_to_float(R.prop(value_key,
                                                          raw_node)),
                             type='Feature',
                             geometry=dict(type='Point', coordinates=location),
                             name=R.prop(node_name_key, raw_node),
                             is_generalized=is_generalized,
                             properties=list(R.keys(properties)),
                             property_values=list(R.values(properties)))
                    ])
            })

    raw_nodes = create_raw_nodes(resource)
    # Reduce the nodes
    nodes_by_stage = R.reduce(
        lambda accum, i_and_node: accumulate_nodes(accum, i_and_node[1],
                                                   i_and_node[0]), {},
        enumerate(raw_nodes))
    nodes = R.flatten(R.values(nodes_by_stage))
    return dict(nodes=nodes,
                nodes_by_stage=nodes_by_stage,
                links=create_links(stages, value_key, nodes_by_stage))
예제 #5
0
 def reduce_or(q_expressions):
     return R.reduce(lambda qs, q: qs | q if qs else q, None, q_expressions)
예제 #6
0
def generate_sankey_data(resource):
    """
        Generates nodes and links for the given Resouce object
    :param resource:  Resource object
    :return: A dict containing nodes and links. nodes are a dict key by stage name
        Results can be assigned to resource.data.sankey and saved
    """

    settings = R.item_path(['data', 'settings'], resource)
    stages = R.prop('stages', settings)
    stage_key = R.prop('stageKey', settings)
    value_key = R.prop('valueKey', settings)
    location_key = R.prop('locationKey', settings)
    node_name_key = R.prop('nodeNameKey', settings)
    node_color_key = R.prop_or(None, 'nodeColorKey', settings)
    default_location = R.prop('defaultLocation', settings)
    delineator = R.prop_or(';', 'delineator', settings)
    # A dct of stages by name
    stage_by_name = stages_by_name(stages)

    link_start_node_key = R.prop_or(None, 'linkStartNodeKey', settings)
    link_end_node_key = R.prop_or(None, 'linkEndNodeKey', settings)
    link_value_key = R.prop_or(None, 'linkValueKey', settings)
    link_color_key = R.prop_or(None, 'linkColorKey', settings)

    def accumulate_nodes(accum, raw_node, i):
        """
            Accumulate each node, keying by the name of the node's stage key
            Since nodes share stage keys these each result is an array of nodes
        :param accum:
        :param raw_node:
        :param i:
        :return:
        """
        location_obj = resolve_coordinates(default_location, R.prop_or(None, location_key, raw_node), i)
        location = R.prop('location', location_obj)
        is_generalized = R.prop('isGeneralized', location_obj)
        # The key where then node is stored is the stage key
        node_stage = raw_node[stage_key]
        # Get key from name or it's already a key
        key = R.prop('key', R.prop_or(dict(key=node_stage), node_stage, stage_by_name))

        # Copy all properties from resource.data  except settings and raw_data
        # Also grab raw_node properties
        # This is for arbitrary properties defined in the data
        # We put them in properties and propertyValues since graphql hates arbitrary key/values
        properties = R.merge(
            R.omit(['settings', 'rawData'], R.prop('data', resource)),
            raw_node
        )
        properties[node_name_key] = humanize(properties[node_name_key])
        return R.merge(
            # Omit accum[key] since we'll concat it with the new node
            R.omit([key], accum),
            {
                # concat accum[key] or [] with the new node
                key: R.concat(
                    R.prop_or([], key, accum),
                    # Note that the value is an array so we can combine nodes with the same stage key
                    [
                        dict(
                            value=string_to_float(R.prop(value_key, raw_node)),
                            type='Feature',
                            geometry=dict(
                                type='Point',
                                coordinates=location
                            ),
                            name=R.prop(node_name_key, raw_node),
                            isGeneralized=is_generalized,
                            properties=list(R.keys(properties)),
                            propertyValues=list(R.values(properties))
                        )
                    ]
                )
            }
        )

    raw_nodes = create_raw_nodes(delineator, resource)

    # Reduce the nodes
    nodes_by_stage = R.reduce(
        lambda accum, i_and_node: accumulate_nodes(accum, i_and_node[1], i_and_node[0]),
        {},
        enumerate(raw_nodes)
    )
    nodes = R.flatten(R.values(nodes_by_stage))
    # See if there are explicit links
    if R.item_path_or(False, ['data', 'settings', 'link_start_node_key'], resource):
        raw_links = create_raw_links(delineator, resource)
        node_key_key = R.prop('nodeNameKey', settings)
        nodes_by_key = R.from_pairs(R.map(
            lambda node: [prop_lookup(node, node_key_key), node],
            nodes
        ))
        links = R.map(
            lambda link: dict(
                source_node=nodes_by_key[link[link_start_node_key]],
                target_node=nodes_by_key[link[link_end_node_key]],
                value=link[link_value_key],
                color=R.prop_or(None, link_color_key, link)
            ),
            raw_links
        )
    else:
        # Guess links from nodes and stages
        links = create_links(stages, value_key, nodes_by_stage)
    return dict(
        nodes=nodes,
        nodes_by_stage=nodes_by_stage,
        # We might have explicit links or have to generate all possible based on the nodes
        links=links
    )
예제 #7
0
 def reduce(values):
     return R.reduce(
         lambda accum, list_values: R.concat(accum,
                                             map_value(list_values)),
         [], values)