From 5bf22fc7e3c392c8bd44315ca2d06d7dca7d084e Mon Sep 17 00:00:00 2001 From: sotech117 Date: Thu, 31 Jul 2025 17:27:24 -0400 Subject: add code for analysis of data --- venv/lib/python3.8/site-packages/dash/_grouping.py | 250 +++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 venv/lib/python3.8/site-packages/dash/_grouping.py (limited to 'venv/lib/python3.8/site-packages/dash/_grouping.py') diff --git a/venv/lib/python3.8/site-packages/dash/_grouping.py b/venv/lib/python3.8/site-packages/dash/_grouping.py new file mode 100644 index 0000000..7271d97 --- /dev/null +++ b/venv/lib/python3.8/site-packages/dash/_grouping.py @@ -0,0 +1,250 @@ +""" +This module contains a collection of utility function for dealing with property +groupings. + +Terminology: + +For the purpose of grouping and ungrouping, tuples/lists and dictionaries are considered +"composite values" and all other values are considered "scalar values". + +A "grouping value" is either composite or scalar. + +A "schema" is a grouping value that can be used to encode an expected grouping +structure + +""" +from .exceptions import InvalidCallbackReturnValue +from ._utils import AttributeDict, stringify_id + + +def flatten_grouping(grouping, schema=None): + """ + Convert a grouping value to a list of scalar values + + :param grouping: grouping value to flatten + :param schema: If provided, a grouping value representing the expected structure of + the input grouping value. If not provided, the grouping value is its own schema. + A schema is required in order to be able treat tuples and dicts in the input + grouping as scalar values. + + :return: list of the scalar values in the input grouping + """ + stack = [] + result = [] + + # Avoid repeated recursive Python calls by using an explicit stack + push = stack.append + pop = stack.pop + + # Only validate once at the top if schema is provided + if schema is None: + schema = grouping + else: + validate_grouping(grouping, schema) + + push((grouping, schema)) + while stack: + group, sch = pop() + # Inline isinstance checks for perf + typ = type(sch) + if typ is tuple or typ is list: + # Avoid double recursion / excessive list construction + for ge, se in zip(group, sch): + push((ge, se)) + elif typ is dict: + for k in sch: + push((group[k], sch[k])) + else: + result.append(group) + result.reverse() # Since we LIFO, leaf values are in reverse order + return result + + +def grouping_len(grouping): + """ + Get the length of a grouping. The length equal to the number of scalar values + contained in the grouping, which is equivalent to the length of the list that would + result from calling flatten_grouping on the grouping value. + + :param grouping: The grouping value to calculate the length of + :return: non-negative integer + """ + if isinstance(grouping, (tuple, list)): + return sum([grouping_len(group_el) for group_el in grouping]) + + if isinstance(grouping, dict): + return sum([grouping_len(group_el) for group_el in grouping.values()]) + + return 1 + + +def make_grouping_by_index(schema, flat_values): + """ + Make a grouping like the provided grouping schema, with scalar values drawn from a + flat list by index. + + Note: Scalar values in schema are not used + + :param schema: Grouping value encoding the structure of the grouping to return + :param flat_values: List of values with length matching the grouping_len of schema. + Elements of flat_values will become the scalar values in the resulting grouping + """ + + def _perform_make_grouping_like(value, next_values): + if isinstance(value, (tuple, list)): + return list( + _perform_make_grouping_like(el, next_values) + for i, el in enumerate(value) + ) + + if isinstance(value, dict): + return { + k: _perform_make_grouping_like(v, next_values) + for i, (k, v) in enumerate(value.items()) + } + + return next_values.pop(0) + + if not isinstance(flat_values, list): + raise ValueError( + "The flat_values argument must be a list. " + f"Received value of type {type(flat_values)}" + ) + + expected_length = len(flatten_grouping(schema)) + if len(flat_values) != expected_length: + raise ValueError( + f"The specified grouping pattern requires {expected_length} " + f"elements but received {len(flat_values)}\n" + f" Grouping pattern: {repr(schema)}\n" + f" Values: {flat_values}" + ) + + return _perform_make_grouping_like(schema, list(flat_values)) + + +def map_grouping(fn, grouping): + """ + Map a function over all of the scalar values of a grouping, maintaining the + grouping structure + + :param fn: Single-argument function that accepts and returns scalar grouping values + :param grouping: The grouping to map the function over + :return: A new grouping with the same structure as input grouping with scalar + values updated by the input function. + """ + if isinstance(grouping, (tuple, list)): + return [map_grouping(fn, g) for g in grouping] + + if isinstance(grouping, dict): + return AttributeDict({k: map_grouping(fn, g) for k, g in grouping.items()}) + + return fn(grouping) + + +def make_grouping_by_key(schema, source, default=None): + """ + Create a grouping from a schema by using the schema's scalar values to look up + items in the provided source object. + + :param schema: A grouping of potential keys in source + :param source: Dict-like object to use to look up scalar grouping value using + scalar grouping values as keys + :param default: Default scalar value to use if grouping scalar key is not present + in source + :return: grouping + """ + return map_grouping(lambda s: source.get(s, default), schema) + + +class SchemaTypeValidationError(InvalidCallbackReturnValue): + def __init__(self, value, full_schema, path, expected_type): + super().__init__( + msg=f""" + Schema: {full_schema} + Path: {repr(path)} + Expected type: {expected_type} + Received value of type {type(value)}: + {repr(value)} + """ + ) + + @classmethod + def check(cls, value, full_schema, path, expected_type): + if not isinstance(value, expected_type): + raise SchemaTypeValidationError(value, full_schema, path, expected_type) + + +class SchemaLengthValidationError(InvalidCallbackReturnValue): + def __init__(self, value, full_schema, path, expected_len): + super().__init__( + msg=f""" + Schema: {full_schema} + Path: {repr(path)} + Expected length: {expected_len} + Received value of length {len(value)}: + {repr(value)} + """ + ) + + @classmethod + def check(cls, value, full_schema, path, expected_len): + if len(value) != expected_len: + raise SchemaLengthValidationError(value, full_schema, path, expected_len) + + +class SchemaKeysValidationError(InvalidCallbackReturnValue): + def __init__(self, value, full_schema, path, expected_keys): + super().__init__( + msg=f""" + Schema: {full_schema} + Path: {repr(path)} + Expected keys: {expected_keys} + Received value with keys {set(value.keys())}: + {repr(value)} + """ + ) + + @classmethod + def check(cls, value, full_schema, path, expected_keys): + if set(value.keys()) != set(expected_keys): + raise SchemaKeysValidationError(value, full_schema, path, expected_keys) + + +def validate_grouping(grouping, schema, full_schema=None, path=()): + """ + Validate that the provided grouping conforms to the provided schema. + If not, raise a SchemaValidationError + """ + if full_schema is None: + full_schema = schema + + if isinstance(schema, (tuple, list)): + SchemaTypeValidationError.check(grouping, full_schema, path, (tuple, list)) + SchemaLengthValidationError.check(grouping, full_schema, path, len(schema)) + + for i, (g, s) in enumerate(zip(grouping, schema)): + validate_grouping(g, s, full_schema=full_schema, path=path + (i,)) + elif isinstance(schema, dict): + SchemaTypeValidationError.check(grouping, full_schema, path, dict) + SchemaKeysValidationError.check(grouping, full_schema, path, set(schema)) + for k in schema: + validate_grouping( + grouping[k], schema[k], full_schema=full_schema, path=path + (k,) + ) + else: + pass + + +def update_args_group(g, triggered): + if isinstance(g, dict): + str_id = stringify_id(g["id"]) + prop_id = f"{str_id}.{g['property']}" + + new_values = { + "value": g.get("value"), + "str_id": str_id, + "triggered": prop_id in triggered, + "id": AttributeDict(g["id"]) if isinstance(g["id"], dict) else g["id"], + } + g.update(new_values) -- cgit v1.2.3-70-g09d2