Source code for bw_timex.helper_classes
import warnings
from loguru import logger
[docs]
class SetList:
"""A helper class for the mapping of the same/mapped activity in different (temporal) databases, composed of a list of sets, that hold can hold the set of tuples of (act_id, database).
It is built by adding sets to the list, and returns the matching sets if one
calls the an item from a set.
Example: If the class instance is called my_setlist, my_setlist.add(set).
"""
def __init__(
self,
) -> None:
self.list = []
[docs]
def add(
self,
new_set: set,
) -> None:
"""
This method adds a set to the SetList instance.
Parameters
-----
new_set: a set to add to the SetList instance
Returns
-------
None
"""
if new_set not in self.list:
checklist_items = [
item for itemset in self.list for item in new_set if item in itemset
]
checklist_sets = [
itemset for itemset in self.list for item in new_set if item in itemset
]
if len(checklist_items) != 0:
logger.info(
f"tried to add {new_set} to the SetList\n, but {checklist_items} already exist in the SetList in:\n {checklist_sets}. \n Skipping {new_set}"
)
else:
self.list.append(new_set)
else:
pass
def __getitem__(self, key: any) -> set:
"""
Returns all sets in the SetList instance containing the key
Inputs
------
key: the key to look for in the sets of the SetList
Returns
-------
A list containing the set or all sets
"""
sets = [matching_set for matching_set in self.list if key in matching_set]
if len(sets) > 1:
logger.warning(
f"Key found in multiple sets! Please check {sets} ! Returning only the first set"
)
if len(sets) > 0:
return sets[0]
warnings.warn(f"Key {key} not found in SetList")
return None
def __len__(
self,
) -> int:
return len(self.list)
def __repr__(self):
return f"SetList({self.list})"
[docs]
class TimeMappingDict(dict):
"""
A dictionary mapping (flow, timestamp) tuples to unique integer IDs.
"""
def __init__(self, start_id=2, *args, **kwargs) -> None:
"""Initializes the dictionary with a starting ID."""
super().__init__(*args, **kwargs)
self._current_id = start_id
self._used_ids = set(self.values()) # Track assigned IDs efficiently
self._modified = False # Track changes
self._reversed_dict = None # Store reversed dict when needed
[docs]
def add(self, process_time_tuple, unique_id=None):
"""
Adds a new process_time_tuple to the dictionary.
Parameters
----------
process_time_tuple : tuple
A tuple of (flow and timestamp)
unique_id : int, optional
A unique ID for the tuple (default: None).
Returns
-------
int
The assigned unique ID.
"""
# If already exists, return immediately
if process_time_tuple in self:
return self[process_time_tuple]
if unique_id is not None:
if unique_id in self._used_ids:
raise ValueError(f"Unique ID {unique_id} is already assigned.")
self._used_ids.add(unique_id)
self[process_time_tuple] = unique_id
else:
new_id = self._current_id
self._current_id += 1
self[process_time_tuple] = new_id
self._used_ids.add(new_id)
self._modified = True # Mark as modified
return self[process_time_tuple]
@property
[docs]
def reversed(self):
"""
Returns a reversed version of the dictionary, updating it only if necessary.
Returns
-------
dict
A reversed dictionary mapping unique IDs to (flow, timestamp) tuples.
"""
if self._modified or self._reversed_dict is None:
self._reversed_dict = {v: k for k, v in self.items()}
self._modified = False # Reset modification flag
return self._reversed_dict
[docs]
class InterDatabaseMapping(dict):
"""
A dictionary of the form {id1:{database1: id1, database2: id2, ...}, id2: ...} that maps the
same activity in different databases.
"""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._reciprocal = False
[docs]
def find_match(self, id_, db_name) -> any:
"""
Returns the matching ID in the given database for the given ID.
"""
return self[id_][db_name]
[docs]
def make_reciprocal(self):
"""
Internal method to make the mapping reciprocal.
"""
if not self._reciprocal:
for mapping in list(self.values()):
for id_ in list(mapping.values()):
if id_ not in self.keys():
self[id_] = mapping
self._reciprocal = True
def __getitem__(self, key):
if not self._reciprocal:
self.make_reciprocal()
return super().__getitem__(key)