An-augmented-Tree-Item

There are 3 basic types of tree items derived from the abstract base class AnAugmentedTreeItem. These 3 tree items represent values, sequences and mappings.

The following example shows nested data and the ‘augmented’ view on it. A tree item has always a primekey. Using schemas this primekey doesn’t necessarily need to be equal to the real index/key within the nested data. The primename is just an association for the tree item. The primevalue of the tree item referees to the nested data the tree item is attached to. So the primevalue of the root tree item always shows the complete nested data.

[1]:
from augmentedtree import AugmentedTree, PRIMARYVALUE_KEY
import json

nested_data = [{"a1": 1}, {"a2": 2, "a3": [3, 4, 5], }]

pretty_printed_data = json.dumps(nested_data, indent="  ")
print(pretty_printed_data)
[
  {
    "a1": 1
  },
  {
    "a2": 2,
    "a3": [
      3,
      4,
      5
    ]
  }
]

Above is a pretty print using json.dumps showing the how the nested data looks like.

The nested data within a 3 column view (primekey, primename) with primevalue (the data of AnAugmentedTreeItem) as an additional column.

[2]:
tree = AugmentedTree(nested_data)
tree.print(additional_columns=[PRIMARYVALUE_KEY])
                                  Primevalue
[..]              ' [{'a1': 1}, {'a2': 2, 'a3': [3, 4, 5]}]
  0.              ' {'a1': 1}
    a1: 1         ' 1
  1.              ' {'a2': 2, 'a3': [3, 4, 5]}
    a2: 2         ' 2
    a3: [3, 4, 5] ' [3, 4, 5]

Value (& Sequence)

A value tree item is being considered any type not being a Mapping.

Sequences will be treated as a value if they do not possess a Mapping. A SequenceTreeItem will behave like a Sequence.

Mappings

The items of a Mapping can be divided into 2 groups. Values which should be shown and metadata which will be hidden. There are 2 possibilities to define this behavior.

Flat Mapping item

A flat mapping item has its values and metadata within the same level.

[3]:
from augmentedtree import MappingSchemaBuilder, AugmentedTree, use_mappingtree_schemas

flat_pizza = {
    "type": "flat-pizza",
    "name": "Salami",
    "number": 62,
    "tomato sauce": "1 scoop",
    "cheese": "A lot",
    "salami": "6 slices",
    "allergens": [10, 17, 24]
}

schema = MappingSchemaBuilder.construct(
    identifier=("type", "flat-pizza"),
    primarykey="type",
    primaryname="name",
    additional_metafieldkeys=["allergens", "number"]
)

use_mappingtree_schemas(schema)

AugmentedTree(flat_pizza).print()
flat-pizza  Salami
  tomato sauce: 1 scoop
  cheese: A lot
  salami: 6 slices

Nested Mapping item

A nested mapping item has is values within a additional level. Metadata is at the root level of this item. This ‘rootlevel’ is skipped in the view.

[4]:
nested_pizza = {
    "type": "nested-pizza",
    "name": "Salami",
    "number": 62,
    "ingredients": {
        "tomato sauce": "1 scoop",
        "cheese": "A lot",
        "salami": "6 slices",
    },
    "allergens": [10, 17, 24]
}

schema = MappingSchemaBuilder.construct(
    identifier=("type", "nested-pizza"),
    primarykey="type",
    primaryname="name",
    outervalues_key="ingredients"
)

use_mappingtree_schemas(schema)

AugmentedTree(nested_pizza).print()
nested-pizza  Salami
  tomato sauce: 1 scoop
  cheese: A lot
  salami: 6 slices

Schemas

Schemas are the essential tool to give mapping a more semantic like view. Any dictionary containing the right set of keys of MappingSchema can be used as a schema. IDENTIFIER of MappingSchema is mandatory. Either METAFIELDKEYS or OUTERVALUES are required additionally. OUTERVALUES always overrules METAFIELDKEYS.

All other entries are optional and do have impact on the representation within the view, which will be shown in the following examples, starting with the ‘flat-pizza’ from above.

Overriding default metafieldkey definition keeps the identifier visible.

[5]:
schema = MappingSchemaBuilder.construct(
    identifier=("type", "flat-pizza"),
    metafieldkeys=["number", "allergens"]
)
use_mappingtree_schemas(schema, override_existing=True)
AugmentedTree(flat_pizza).print()
{..}
  type: flat-pizza
  name: Salami
  tomato sauce: 1 scoop
  cheese: A lot
  salami: 6 slices

Adding metafieldkeys to the defaults hides the identifier.

[6]:
schema = MappingSchemaBuilder.construct(
    identifier=("type", "flat-pizza"),
    additional_metafieldkeys=["number", "allergens"]
)
use_mappingtree_schemas(schema, override_existing=True)
AugmentedTree(flat_pizza).print()
{..}
  name: Salami
  tomato sauce: 1 scoop
  cheese: A lot
  salami: 6 slices

Supplied primarykey and primaryname are default metadatakeys hiding both items automatically.

[7]:
schema = MappingSchemaBuilder.construct(
    identifier=("type", "flat-pizza"),
    primarykey="type",
    primaryname="name",
    additional_metafieldkeys=["number", "allergens"]
)
use_mappingtree_schemas(schema, override_existing=True)
AugmentedTree(flat_pizza).print()
flat-pizza  Salami
  tomato sauce: 1 scoop
  cheese: A lot
  salami: 6 slices

Outervalues overrules metadatakeys. If an item of a mapping is defined to represent the real values (or children) of this collection, then automatically all root level items of this mapping are rendered to metadata.

[8]:
schema = MappingSchemaBuilder.construct(
    identifier=("type", "nested-pizza"),
    metafieldkeys=["these", "are", "now", "irrelevant"],
    outervalues_key="ingredients"
)
use_mappingtree_schemas(schema, override_existing=True)
AugmentedTree(nested_pizza).print(additional_columns=["@name", "@allergens"])
                          @name     @allergens
{..}                    ' Salami ' [10, 17, 24]
  tomato sauce: 1 scoop ' Salami ' [10, 17, 24]
  cheese: A lot         ' Salami ' [10, 17, 24]
  salami: 6 slices      ' Salami ' [10, 17, 24]

Paths within the augmentation

AnAugmentedTreeItem has 2 paths. It’s real path is the location within the original nested datastructure the tree items are attached to. The tree item’s augmented path depicts the location within the augmentation changed by schemas.

[9]:
from dicthandling import read_from_json_file

# get nested data and schema for the example
nested_data = read_from_json_file("resources/nested_data_of_examples.json", "AATI/path_example")

tree_without = AugmentedTree(nested_data, use_schemas=False)
tree_without.print()
{..}
  The story:
    type: example
    name: W.
    key: Richard
    items:
      0.
        type: example
        name: steed
        key: mighty
        items:
          0.
            type: example
            name: along
            key: rides
            items:
              into: the sunset

From the data above the last item with the primekey into is selected, the item’s TreePath retrieved and both real path as well augmented path shown. Since no schemas were applied, both are identical.

[10]:
real_and_augmented_path_is_identical = tree_without.select("into")
items_treepath = real_and_augmented_path_is_identical.paths[0]

print("     real path:", items_treepath.real_path)
print("augmented path:", items_treepath.augmented_path)
     real path: /The story/items/0/items/0/items/into
augmented path: /The story/items/0/items/0/items/into

Now the whole process will be repeated, but this time a schema is applied to the nested data, resulting in a different view off it. Both paths differs in this example, due to the schema.

The exemplary data can be found within a json-file.

[11]:
schema_kwargs = read_from_json_file("resources/nested_data_of_examples.json", "AATI/path_example-schema")
example_schema = MappingSchemaBuilder.construct(**schema_kwargs)

use_mappingtree_schemas(example_schema)

tree_with_schema = AugmentedTree(nested_data)
tree_with_schema.print()

augmented_path_differs_from_real = tree_with_schema.select("into")
items_treepath = augmented_path_differs_from_real.paths[0]

print("     real path:", items_treepath.real_path)
print("augmented path:", items_treepath.augmented_path)
{..}
  Richard: W.
    mighty: steed
      rides: along
        into: the sunset

     real path: /The story/items/0/items/0/items/into
augmented path: /Richard/mighty/rides/into
[12]:
# Using the origin data and navigating through it
print(tree_with_schema["The story"]["items"][0]["items"][0]["items"]["into"])

# Using the augmented path
print(tree_with_schema["Richard/mighty/rides/into"])

the sunset
the sunset