| 6 | |
| 7 | |
| 8 | class ClassHandler: |
| 9 | def __init__(self, subclasses, index_to_class, qcode_idx_to_class_idx, qcode_to_idx): |
| 10 | # class -> [parent_classes] |
| 11 | # Dict[str, List[str]] |
| 12 | self.subclasses = subclasses |
| 13 | self.index_to_class = index_to_class |
| 14 | self.qcode_idx_to_class_idx = qcode_idx_to_class_idx |
| 15 | self.qcode_to_idx = qcode_to_idx |
| 16 | |
| 17 | # workaround - @lru_cache does not work with instances methods so basic implementation is done here |
| 18 | self._MAX_CACHE_ITEMS = 10000000 |
| 19 | self._prune_classes_cache = {} |
| 20 | self._get_implied_classes_cache = {} |
| 21 | |
| 22 | def _get_implied_classes( |
| 23 | self, direct_classes: FrozenSet[str], remove_self=True |
| 24 | ) -> FrozenSet[str]: |
| 25 | """ |
| 26 | From a set of (direct) classes this method will generate all of the classes that can be implied. |
| 27 | When remove_self is True it means that a class cannot be implied from itself (but it can still be implied |
| 28 | by other of the direct classes). |
| 29 | :param direct_classes: the set of classes for implied classes to be generated from |
| 30 | :param remove_self: when true a classes implication is not reflexive (e.g. human does not imply human) |
| 31 | :return: set of classes that can be implied from direct_classes |
| 32 | """ |
| 33 | cache_key = (direct_classes, remove_self) |
| 34 | if cache_key in self._get_implied_classes_cache: |
| 35 | return self._get_implied_classes_cache[cache_key] |
| 36 | |
| 37 | if remove_self: |
| 38 | all_implied_classes = set() |
| 39 | else: |
| 40 | all_implied_classes = set(direct_classes) |
| 41 | |
| 42 | # keep track of the classes that have been explored to prevent work from being repeated |
| 43 | explored_classes = set() |
| 44 | for direct_class in direct_classes: |
| 45 | implied_classes = self._explore_class_tree(direct_class, frozenset(explored_classes)) |
| 46 | if remove_self: |
| 47 | implied_classes = implied_classes - {direct_class} |
| 48 | |
| 49 | explored_classes.update(implied_classes) |
| 50 | all_implied_classes.update(implied_classes) |
| 51 | |
| 52 | result = frozenset(all_implied_classes) |
| 53 | self._get_implied_classes_cache[cache_key] = result |
| 54 | if len(self._get_implied_classes_cache) > self._MAX_CACHE_ITEMS: |
| 55 | self._get_implied_classes_cache.popitem() |
| 56 | return result |
| 57 | |
| 58 | def _explore_class_tree( |
| 59 | self, class_id: str, explored_classes: FrozenSet[str] |
| 60 | ) -> FrozenSet[str]: |
| 61 | """ |
| 62 | Recursively explores the class hierarchy (parent classes, parent of parents, etc.) |
| 63 | Returns all the explored classes (these are all impliable from the class provided as an argument (class_id)) |
| 64 | :param class_id: class id for class to explore |
| 65 | :param explored_classes: the classes impliable from class_id |
no outgoing calls
no test coverage detected