Skip to content

Commit

Permalink
Improve tag replacement (#201)
Browse files Browse the repository at this point in the history
* Add flattened_dict property

* Improve and simplify tag replacement

* Use Code.flattened_dict to simplify Codelist.codelist_repr

* Remove 'weight' from test tag codelist to test improved tag replacement

* Make tag codelist test more precise

* Fix Codelist.codelist_repr

* Add docs for improved tag replacement.

* Further update tag codelist docs

* Appease stickler

* Apply suggestions from code review

Co-authored-by: Daniel Huppmann <[email protected]>

* Remove unused parts from test tag codelist

Co-authored-by: Daniel Huppmann <[email protected]>
  • Loading branch information
phackstock and danielhuppmann authored Nov 22, 2022
1 parent 98b1fb0 commit a17beae
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 38 deletions.
29 changes: 26 additions & 3 deletions doc/source/user_guide/codelist.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,29 @@ to be used as tag. The files defining the tags must be named like ``tag_*.yaml``
- Some Key:
description: a short description of the key
When importing a tagged codelist, any occurrence of ``{Tag}`` in the name of a code will
be replaced by every element in the Tag dictionary. The ``{Tag}`` will also be replaced
in any of the code attributes.
When importing a tagged codelist, any code containing ``{Tag}`` in its name will
be replaced by the name of the corresponding tag. In addition any code attributes
containing ``{Tag}`` will also be replaced. If the corresponding tag contains the attribute, this will be used for substitution, otherwise the tag name is used.

To illustrate, consider the following variable:

.. code:: yaml
- Variable|{Tag}:
description: The description contains {Tag}
weight: {Tag}
which will be translated to:

.. code:: yaml
- Variable|Some Key:
description: The description contains a short description of the key
weight: Some Key
Since the tag contains a *description* attribute with value *a short description of the
key* this is substituted in the *description* of the variable.

In contrast, the variable contains the attribute *weight* which features ``{Tag}``,
which is not present in the tag. In this case the tag name *Some Key* is used.
46 changes: 25 additions & 21 deletions nomenclature/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ def contains_tags(self) -> bool:
def tags(self):
return re.findall("(?<={).*?(?=})", self.name)

@property
def flattened_dict(self):
return {
**{k: v for k, v in self.dict().items() if k != "extra_attributes"},
**self.extra_attributes,
}

def replace_tag(self, tag: str, target: "Code") -> "Code":
"""Return a new instance with tag applied
Expand All @@ -84,27 +91,24 @@ def replace_tag(self, tag: str, target: "Code") -> "Code":
New Code instance with occurrences of "{tag}" replaced by target
"""

mapping = {
key: value
for key, value in self.dict().items()
if key != "extra_attributes"
}
# replace name and description
mapping["name"] = mapping["name"].replace("{" + tag + "}", target.name)
mapping["description"] = mapping["description"].replace(
"{" + tag + "}", target.description
)

# replace any other attribute
extra_attributes = self.extra_attributes.copy()
for attr, value in target.extra_attributes.items():
if isinstance(extra_attributes.get(attr), str):
extra_attributes[attr] = extra_attributes[attr].replace(
"{" + tag + "}", value
)
elif isinstance(mapping.get(attr), str):
mapping[attr] = mapping[attr].replace("{" + tag + "}", value)
return self.__class__(**mapping, extra_attributes=extra_attributes)
mapping = {}
for attr, value in self.flattened_dict.items():
# if the attribute is a string and contains "{tag}" replace
if isinstance(value, str) and "{" + tag + "}" in value:
# if the the target has the corresponding attribute replace
if attr in target.flattened_dict:
mapping[attr] = value.replace(
"{" + tag + "}", getattr(target, attr)
)
# otherwise insert the name
else:
mapping[attr] = value.replace("{" + tag + "}", target.name)
# otherwise append as is
else:
mapping[attr] = value
name = mapping["name"]
del mapping["name"]
return self.__class__.from_dict({name: mapping})

def __getattr__(self, k):
try:
Expand Down
13 changes: 6 additions & 7 deletions nomenclature/codelist.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,13 +362,12 @@ def codelist_repr(self) -> Dict:

nice_dict = {}
for name, code in self.mapping.items():
code_dict = code.dict()
del code_dict["name"]
for attr, value in code.dict().items():
if value is None and attr != "unit":
del code_dict[attr]
code_dict.update(code_dict["extra_attributes"])
del code_dict["extra_attributes"]
code_dict = {
k: v
for k, v in code.flattened_dict.items()
if (v is not None and k != "name") or k == "unit"
}

nice_dict[name] = code_dict

return nice_dict
Expand Down
3 changes: 0 additions & 3 deletions tests/data/tagged_codelist/tag_sector.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
- Sector:
- Energy:
definition: energy
value_bool: true
value_number: 2.3
weight: Energy
- Industry:
definition: industrial
weight: Energy
23 changes: 19 additions & 4 deletions tests/test_codelist.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,25 @@ def test_tagged_codelist():
"variable", TEST_DATA_DIR / "tagged_codelist"
)

v = "Final Energy|Industry|Renewables"
d = "Final energy consumption of renewables in the industrial sector"
assert v in code
assert code[v].description == d
exp = {
"Final Energy|Industry|Renewables": {
"description": (
"Final energy consumption of renewables in the industrial sector"
),
"weight": "Final Energy|Industry",
},
"Final Energy|Energy|Renewables": {
"description": (
"Final energy consumption of renewables in the energy sector"
),
"weight": "Final Energy|Energy",
},
}

for code_name, attrs in exp.items():
assert code_name in code
for attr_name, value in attrs.items():
assert getattr(code[code_name], attr_name) == value


def test_region_codelist():
Expand Down

0 comments on commit a17beae

Please sign in to comment.