diff --git a/persper/analytics2/abstractions/callcommitgraph.py b/persper/analytics2/abstractions/callcommitgraph.py index 1e549703100..551a82a0b60 100644 --- a/persper/analytics2/abstractions/callcommitgraph.py +++ b/persper/analytics2/abstractions/callcommitgraph.py @@ -242,6 +242,7 @@ def update_node_history(self, node_id: NodeId, commit_hexsha: str, added_lines: If commit_hexsha doesn't exist in history, add the entry to history. If commit_hexsha exists in history, the entry will be *replaced*. Note the entire node history entry of this hexsha will be replaced rather than merged. + To accumulate multiple modifications to a node in the same commit, use `NodeHistoryAccumulator` helper class. """ pass diff --git a/persper/analytics2/utilities.py b/persper/analytics2/utilities.py new file mode 100644 index 00000000000..15c5c6c14b9 --- /dev/null +++ b/persper/analytics2/utilities.py @@ -0,0 +1,62 @@ +from persper.analytics2.abstractions.callcommitgraph import IWriteOnlyCallCommitGraph, NodeId + + +class NodeHistoryAccumulator(): + """ + Provides convenient methods for accumulating node history. + (i.e. the added/removed lines to the same node in a single commit) + """ + + def __init__(self): + # [NodeId]: [added_lines, removed_lines] + self._nodes = {} + + def clear(self): + """ + Clears all the accumulated histroy information contained in this instance. + """ + self._nodes.clear() + + def add(self, node_id: NodeId, added_lines: int = 0, removed_lines: int = 0): + """ + Accumulates the added/removed lines of code to the specific node_id. + """ + info = self._nodes.get(node_id, None) + if info == None: + if not isinstance(node_id, NodeId): + raise ValueError("node_id should be NodeId.") + if not isinstance(added_lines, int): + raise ValueError("added_lines should be int.") + if not isinstance(removed_lines, int): + raise ValueError("removed_lines should be int.") + if added_lines != 0 or removed_lines != 0: + info = [added_lines, removed_lines] + self._nodes[node_id] = info + else: + info[0] += added_lines + info[1] += removed_lines + + def get(self, node_id: NodeId): + """ + Gets the accumulated added/removed lines of code for the specified node ID. + returns + (added_lines: int, removed_lines: int) + """ + info = self._nodes.get(node_id, None) + if info == None: + if not isinstance(node_id, NodeId): + raise ValueError("node_id should be NodeId.") + return 0, 0 + return info[0], info[1] + + def apply(self, graph: IWriteOnlyCallCommitGraph, commit_hexsha: str): + """ + Applies the node history contained in this instance to the specified call commit graph. + params + graph: the call commit graph to be updated. + commit_hexsha: When updating the call commit graph, specify the current commit hexsha. + remarks + You may want to call `clear` to reset the change history after calling this method. + """ + for id, (added, removed) in self._nodes: + graph.update_node_history(id, commit_hexsha, added, removed) diff --git a/test/analytics2/utilities.py b/test/analytics2/utilities.py new file mode 100644 index 00000000000..85228c745d5 --- /dev/null +++ b/test/analytics2/utilities.py @@ -0,0 +1,20 @@ +from persper.analytics2.utilities import NodeHistoryAccumulator +from persper.analytics2.abstractions.callcommitgraph import NodeId + + +def test_node_history_accumulator(): + nodeHistory = NodeHistoryAccumulator() + testId0 = NodeId("CTest0", "cpp") + testId1 = NodeId("CTest1", "cpp") + testId2 = NodeId("CTest2", "cpp") + nodeHistory.add(testId1, 10, 20) + nodeHistory.add(testId2, -10, 20) + nodeHistory.add(testId1, 5, -5) + assert nodeHistory.get(testId0) == (0, 0) + assert nodeHistory.get(testId1) == (15, 15) + assert nodeHistory.get(testId2) == (-10, 20) + # TODO test `apply` with MemoryCCG + nodeHistory.clear() + assert nodeHistory.get(testId0) == (0, 0) + assert nodeHistory.get(testId1) == (0, 0) + assert nodeHistory.get(testId2) == (0, 0)