Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ You could also use python's built-in `filter` function:

```python
assets_facts = list(
filter(lambda fact: fact.get_concept().get_value() == assets_concept, all_facts)
filter(lambda fact: fact.get_concept() == assets_concept, all_facts)
)
# or

Expand Down
47 changes: 25 additions & 22 deletions brel/brel_fact.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
from typing import Any, List, Optional, cast

from brel import Context
from brel.reportelements import Concept
from brel.characteristics import (
Aspect,
ConceptCharacteristic,
ICharacteristic,
ConceptCharacteristic,
PeriodCharacteristic,
UnitCharacteristic,
EntityCharacteristic,
Expand Down Expand Up @@ -71,9 +72,7 @@ def get_context(self) -> Context:
return self.__context

def get_value_as_str(self) -> str:
"""
:returns str: The value of the fact as a string.
"""
"""[DEPRECATED] Use str() instead."""
return self.__value

def get_value_as_int(self) -> int:
Expand Down Expand Up @@ -126,45 +125,49 @@ def __bool__(self) -> bool:
f"Fact {self.__id} does not have a bool value. It has value {self.__value}, which does not resolve to a bool."
)

def get_value(self) -> str:
def get_value(self) -> Any:
"""
:returns Any: The value of the fact. The type of the value depends on the type of the fact.
"""
return self.__value
if self.get_concept().is_integer():
return int(self)
elif self.get_concept().is_numeric():
return float(self)
elif self.get_concept().is_boolean():
return bool(self)
else:
return self.__value

def get_precision(self) -> float | None:
"""
:returns Any: The precision of the fact. Only applies to numeric facts.
:returns float: The precision of the fact. Only applies to numeric facts.
"""
return self.__precision

def get_decimals(self) -> float | None:
"""
:returns Any: The decimals property of the fact. Only applies to numeric facts.
:returns float: The decimals property of the fact. Only applies to numeric facts.
"""
return self.__decimals

def __str__(self) -> str:
"""
:returns str: The fact represented as a string.
:returns str: The fact value as a string.
"""
output = ""
for aspect in self.__context.get_aspects():
aspect_name = aspect.get_name()
aspect_value = self.__context.get_characteristic(aspect)
output += f"{aspect_name}: {aspect_value}, "
output += f"value: {self.__value}"
return output
return self.__value

# 2nd class citizens
def get_concept(self) -> ConceptCharacteristic:
def get_concept(self) -> Concept:
"""
:returns ConceptCharacteristic: The concept characteristic of the facts context.
Equivalent to calling `fact.get_context().get_concept()`
:returns Concept: The concept of the facts context.
Equivalent to calling `fact.get_context().get_concept().get_value()`
"""
concept: ConceptCharacteristic = cast(
ConceptCharacteristic,
self.__context.get_characteristic(Aspect.CONCEPT),
concept_characteristic = cast(
ConceptCharacteristic, self.__context.get_characteristic(Aspect.CONCEPT)
)
concept: Concept = cast(
Concept,
concept_characteristic.get_value(),
)
return concept

Expand Down
8 changes: 2 additions & 6 deletions brel/brel_filing.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def get_all_reported_concepts(self) -> List[Concept]:
"""
reported_concepts: list[Concept] = []
for fact in self.get_all_facts():
concept = fact.get_concept().get_value()
concept = fact.get_concept()
if concept not in reported_concepts:
reported_concepts.append(concept)

Expand All @@ -374,11 +374,7 @@ def get_facts_by_concept_name(self, concept_name: QName | str) -> List[Fact]:
concept_name, Concept
)

return [
fact
for fact in self.get_all_facts()
if fact.get_concept().get_value() == concept
]
return [fact for fact in self.get_all_facts() if fact.get_concept() == concept]

def get_facts_by_concept(self, concept: Concept) -> List[Fact]:
"""
Expand Down
5 changes: 2 additions & 3 deletions brel/networks/calculation_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def is_subnetwork_aggregation_consistent(
concept = node.get_concept()
node_facts = list(
filter(
lambda fact: fact.get_concept().get_value() == concept,
lambda fact: fact.get_concept() == concept,
facts,
)
)
Expand All @@ -153,8 +153,7 @@ def is_subnetwork_aggregation_consistent(
if node_aspect == Aspect.CONCEPT:
child_facts = list(
filter(
lambda fact: fact.get_concept().get_value()
== child_concept,
lambda fact: fact.get_concept() == child_concept,
child_facts,
)
)
Expand Down
144 changes: 144 additions & 0 deletions brel/reportelements/concept.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,122 @@
from brel.resource import BrelLabel
from brel.services.translation.translation_service import TranslationService

textual_types = [
"domainItemType",
"escapedItemType",
"xmlNodesItemType",
"xmlItemType",
"textBlockItemType",
"guidanceItemType",
"noLangTokenItemType",
"noLangStringItemType",
"prefixedContentItemType",
"prefixedContentType",
"SQNameItemType",
"SQNameType",
"gYearListItemType",
"dateTimeItemType",
"fractionItemType",
"stringItemType",
"booleanItemType",
"hexBinaryItemType",
"base64BinaryItemType",
"anyURIItemType",
"QNameItemType",
"durationItemType",
"dateTimeItemType",
"timeItemType",
"dateItemType",
"gYearMonthItemType",
"gYearItemType",
"gMonthDayItemType",
"gDayItemType",
"gMonthItemType",
"normalizedStringItemType",
"tokenItemType",
"languageItemType",
"NameItemType",
"NCNameItemType",
"financialInstrumentGlobalIdentifierItemType",
]

numeric_types = [
"percentItemType",
"perShareItemType",
"areaItemType",
"volumeItemType",
"massItemType",
"weightItemType",
"energyItemType",
"powerItemType",
"lengthItemType",
"memoryItemType",
"noDecimalsMonetaryItemType",
"nonNegativeMonetaryItemType",
"nonNegativeNoDecimalsMonetaryItemType",
"insolationItemType",
"temperatureItemType",
"pressureItemType",
"frequencyItemType",
"irradianceItemType",
"speedItemType",
"planeAngleItemType",
"voltageItemType",
"electricCurrentItemType",
"forceItemType",
"electricChargeItemType",
"flowItemType",
"massFlowItemType",
"monetaryPerLengthItemType",
"monetaryPerAreaItemType",
"monetaryPerVolumeItemType",
"monetaryPerDurationItemType",
"monetaryPerEnergyItemType",
"monetaryPerMassItemType",
"ghgEmissionsItemType",
"energyPerMonetaryItemType",
"ghgEmissionsPerMonetaryItemType",
"volumePerMonetaryItemType",
"decimalItemType",
"floatItemType",
"doubleItemType",
"integerItemType",
"nonPositiveIntegerItemType",
"negativeIntegerItemType",
"longItemType",
"intItemType",
"shortItemType",
"byteItemType",
"nonNegativeIntegerItemType",
"unsignedLongItemType",
"unsignedIntItemType",
"unsignedShortItemType",
"unsignedByteItemType",
"positiveIntegerItemType",
"monetaryItemType",
"sharesItemType",
"pureItemType",
"perUnitItemType",
]

integer_types = [
"noDecimalsMonetaryItemType",
"nonNegativeNoDecimalsMonetaryItemType",
"integerItemType",
"nonPositiveIntegerItemType",
"negativeIntegerItemType",
"longItemType",
"intItemType",
"shortItemType",
"byteItemType",
"nonNegativeIntegerItemType",
"unsignedLongItemType",
"unsignedIntItemType",
"unsignedShortItemType",
"unsignedByteItemType",
"positiveIntegerItemType",
]


class Concept(IReportElement):
"""
Expand Down Expand Up @@ -107,6 +223,34 @@ def get_data_type(self) -> str:
"""
return self.__data_type

def is_textual(self) -> bool:
"""
Check if the concept is of a textual type.
:returns bool: True 'IFF' the concept is of a textual type, False otherwise
"""
return self.__data_type.split(":")[-1] in textual_types

def is_numeric(self) -> bool:
"""
Check if the concept is of a numeric type.
:returns bool: True 'IFF' the concept is of a numeric type, False otherwise
"""
return self.__data_type.split(":")[-1] in numeric_types

def is_integer(self) -> bool:
"""
Check if the concept is of an integer type.
:returns bool: True 'IFF' the concept is of an integer type, False otherwise
"""
return self.__data_type.split(":")[-1] in integer_types

def is_boolean(self) -> bool:
"""
Check if the concept is of a boolean type.
:returns bool: True 'IFF' the concept is of a boolean type, False otherwise
"""
return self.__data_type.split(":")[-1] == "booleanItemType"

def get_balance_type(self) -> str | None:
"""
Get the balance type of the concept.
Expand Down
21 changes: 10 additions & 11 deletions tests/core_tests/test_fact.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""

import brel
import json


def test_qname_getters():
Expand All @@ -17,12 +18,12 @@ def test_qname_getters():

# check if a fact with value = "true" is parsed correctly as a bool
fact = filing.get_facts_by_concept_name("dei:DocumentQuarterlyReport")[0]
assert fact._get_id() == "f-2", "Expected fact id to be 'f-2'" # type: ignore
assert fact.get_id() == "f-2", "Expected fact id to be 'f-2'" # type: ignore

context = fact.get_context()
assert context._get_id() == "c-1", "Expected context id to be 'c-1'" # type: ignore

assert fact.get_value_as_str() == "true", "Expected 'true' as fact value is 'true'"
assert str(fact) == "true", "Expected 'true' as fact value is 'true'"
assert bool(fact) == True, "Expected True as fact value is 'true'"
try:
int(fact)
Expand All @@ -47,25 +48,23 @@ def test_qname_getters():
fact.get_entity()
), "Expected '320193' to be in fact entity string"

fact_str = str(fact)
assert "concept" in fact_str, "Expected 'concept' to be in fact string"
assert "period" in fact_str, "Expected 'period' to be in fact string"
assert "entity" in fact_str, "Expected 'entity' to be in fact string"
assert "unit" not in fact_str, "Expected 'unit' not to be in fact string"
fact_str = json.dumps(dict(fact))
assert "concept" in fact_str, "Expected 'concept' to be in fact dict"
assert "period" in fact_str, "Expected 'period' to be in fact dict"
assert "entity" in fact_str, "Expected 'entity' to be in fact dict"
assert "unit" not in fact_str, "Expected 'unit' not to be in fact dict"

# check if parsing a false fact as bool works
fact = filing.get_facts_by_concept_name("dei:AmendmentFlag")[0]
assert (
fact.get_value_as_str() == "false"
), "Expected 'false' as fact value is 'false'"
assert str(fact) == "false", "Expected 'false' as fact value is 'false'"
assert bool(fact) == False, "Expected False as fact value is 'false'"

# check for an integer fact
fact = filing.get_facts_by_concept_name("us-gaap:GrossProfit")[0]

assert isinstance(int(fact), int), "Expected int as fact value is int"
assert int(fact) > 1000000, "Expected apples gross profit to be > 1M"
assert isinstance(float(fact), float), "Expected float as fact value is float"
assert isinstance(fact.get_value(), float), "Expected float as fact value is float"
assert float(fact) > 1000000, "Expected apples gross profit to be > 1M"
try: # check if parsing a false fact as bool works
bool(fact)
Expand Down
4 changes: 2 additions & 2 deletions tests/core_tests/test_filing.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ def test_filing_getters():
facts = filing.get_facts_by_concept_name("ete:cash")
assert_list_of_type(facts, Fact)
assert all(
f.get_concept().get_value() == concept for f in facts
f.get_concept() == concept for f in facts
), "Expected all facts to have the cash concept"

# check get_facts_by_concept(). all facts should have the cash concept
facts = filing.get_facts_by_concept(concept)
assert_list_of_type(facts, Fact)
assert all(
f.get_concept().get_value() == concept for f in facts
f.get_concept() == concept for f in facts
), "Expected all facts to have the cash concept"

# check if get_all_component_uris() contains the uris "http://foo/role/balance", "http://foo/role/hypercube" and "http://foo/role/bad-balance"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def test_end_to_end_fact_f013():
fact_dimension = fact.get_characteristic(dimension_aspect)

# check that the concept is ete:balance, the period is duration and the entity is 1234
assert fact_concept.get_value() == filing.get_concept("ete:concept1")
assert fact_concept == filing.get_concept("ete:concept1")
assert fact_period is not None
assert fact_period.is_instant() is True
assert fact_period.get_instant_period() == date(2024, 5, 3)
Expand Down
Loading