Tech Meeting 2024 Q1 - Enumerations#
The technical meeting that took place on 2024.04.03 related to the Q1 sprints covered the following topics:
Running an analysis whilst modifying the
.ini
configuration files.Usage of enumerations within
ra2ce
.Discussion on long term views for
ra2ce
subprojects.Walk-through, on how to add new analyses to the current solution.
This jupyter notebook will cover the second point.
2. Usage of enumerations within ra2ce
#
Enumerations (enum) are introduced in ra2ce
mostly to declare, and therefore distinguish, all the different properties that may influence the creation of a network
or the run of an analysis
.
These enums are, in short, the different possibilities that can be found at the network.ini
or analysis.ini
section properties.
During development of new functionalities, or introduction of new configuration properties, more enumerations could be created or current ones extended. We will see how this works in this section.
2.1. Available enums#
At the current version v0.8.1
the following enumerations are present:
In ra2ce.network.network_config_data.enums
:
AggregateWlEnum,
NetworkTypeEnum,
PartOfDayEnum,
RoadTypeEnum,
SourceEnum
[ ]:
from ra2ce.network.network_config_data.enums.network_type_enum import NetworkTypeEnum
for _network_type in NetworkTypeEnum:
print(_network_type)
In ra2ce.analysis.analysis_config_data.enums
:
AnalysisDirectEnum,
AnalysisIndirectEnum,
DamageCurveEnum,
EventTypeEnum,
LossTypeEnum,
RiskCalculationModeEnum,
TripPurposesEnum,
WeighingEnum
[ ]:
from ra2ce.analysis.analysis_config_data.enums.analysis_losses_enum import AnalysisLossesEnum
for _losses_type in AnalysisLossesEnum:
print(_losses_type)
2.2. Ra2ceEnumBase#
In order to normalize how we handle all our enumerations, a base class Ra2ceEnumBase
(ra2ce.configuration.ra2ce_enum_base
) is created (not that this is not an abstract class).
All our enumerations implement said base class and therefore inherit its methods and definitions.
2.2.1. Creating a new enum#
We can check its functionality by first creating our own enumeration here.
[ ]:
from ra2ce.configuration.ra2ce_enum_base import Ra2ceEnumBase
class DocumentationEnum(Ra2ceEnumBase):
NONE = 0
ALL = 1
EXAMPLE = 2
RESEARCH = 3
TECH_MEETING = 4
ARCHITECTURE = 5
DOMAIN = 6
INVALID = 99
In our enumeration we see two sides, the name that we will use throughout the code (left side) and its associated value (right side). In short, we are just binding said names to a unique value so that python handles everything internally whilst we can write ‘readable’ code.
Some pointers for creation / modification of enums here:
Add new properties after the latest logical value. Sometimes you may find the last two values are
BANANA = 3
andMIX = 100
, choose then to add the next value asPINEAPPLE = 4
instead ofPINEAPPLE = 101
.Always define the names with capital letters.
Try to use simplify the names whilst still being readable.
Do not modify existing values unless previously discussed with the development team.
When inheriting from
Ra2ceEnumBase
:NONE=0
is an optional entry, whilstINVALID=99
is a mandatory one.
2.2.2. Using the inherited methods / properties#
We can now start testing what the Ra2ceEnumBase
does with our DocumentationEnum
example
get_enum(str|None)
will help us getting the associatedra2ce
enum for a given string.
[ ]:
assert DocumentationEnum.get_enum("banana") == DocumentationEnum.INVALID
assert DocumentationEnum.get_enum(None) == DocumentationEnum.NONE
assert DocumentationEnum.get_enum("") == DocumentationEnum.NONE
assert DocumentationEnum.get_enum("reSEarch") == DocumentationEnum.RESEARCH
is_valid()
verifies whether the selected enum property is something we consider ‘valid’. This method can be later on ‘overriden’ so that we could determine more options which are not ‘valid’ ones.
[ ]:
assert DocumentationEnum.NONE.is_valid()
assert DocumentationEnum.INVALID.is_valid() is False
[ ]:
class DocumentationEnumIsValidOverriden(Ra2ceEnumBase):
NONE = 0
ALL = 1
EXAMPLE = 2
RESEARCH = 3
TECH_MEETING = 4
ARCHITECTURE = 5
DOMAIN = 6
INVALID = 99
def is_valid(self) -> bool:
# We extend the current definition so that both
# `EXAMPLE` and `TECH_MEETING` are not consider valid documents.
if self in [
DocumentationEnumIsValidOverriden.EXAMPLE,
DocumentationEnumIsValidOverriden.TECH_MEETING]:
return False
return super().is_valid()
assert DocumentationEnumIsValidOverriden.TECH_MEETING.is_valid() is False
assert DocumentationEnumIsValidOverriden.EXAMPLE.is_valid() is False
assert DocumentationEnumIsValidOverriden.ARCHITECTURE.is_valid()
list_valid_options()
returns all the options mapped as ‘valid’
[ ]:
print("Default valid options:")
for _valid_option in DocumentationEnum.list_valid_options():
print(_valid_option)
print("\nValid options when overriding:")
for _valid_option in DocumentationEnumIsValidOverriden.list_valid_options():
print(_valid_option)
config_value
will return how the enum is to be represented in the config file (.ini
).
[ ]:
for _option in DocumentationEnum:
print(f"{_option}: {_option.config_value}")
But again, this could be easily overriden if desired.
[ ]:
class DocumentationEnumConfigValueOverriden(Ra2ceEnumBase):
NONE = 0
ALL = 1
EXAMPLE = 2
RESEARCH = 3
TECH_MEETING = 4
ARCHITECTURE = 5
DOMAIN = 6
INVALID = 99
@property
def config_value(self) -> str | None:
if "_" in self.name:
_words = [_word.capitalize() for _word in self.name.split("_")]
return " ".join(_words)
return super().__str__()
print(DocumentationEnumConfigValueOverriden.TECH_MEETING)