bugbug icon indicating copy to clipboard operation
bugbug copied to clipboard

[model:component] Add initial product and component as features

Open benjaminmah opened this issue 1 year ago • 5 comments

Resolves #4297.

Includes the initial manually classified product and component of a bug as features.

benjaminmah avatar Jul 03 '24 15:07 benjaminmah

Metrics of the model with the features included: metrics.log

Consistently achieves 0.59 for accuracy and recall for all confidence thresholds.

benjaminmah avatar Jul 03 '24 15:07 benjaminmah

Instead of having specific features for this, you could make sure the function to restore the bug to its initial version also handles changes to product and component: https://github.com/mozilla/bugbug/blob/b7b96b574408f48e23b41cfbfabe4c650850e097/bugbug/bug_snapshot.py#L584.

marco-c avatar Jul 18 '24 11:07 marco-c

Instead of having specific features for this, you could make sure the function to restore the bug to its initial version also handles changes to product and component:

Should I create an issue for this? @marco-c

benjaminmah avatar Jul 22 '24 17:07 benjaminmah

Instead of having specific features for this, you could make sure the function to restore the bug to its initial version also handles changes to product and component:

Should I create an issue for this? @marco-c

Yes thanks!

marco-c avatar Jul 23 '24 08:07 marco-c

Instead of having specific features for this, you could make sure the function to restore the bug to its initial version also handles changes to product and component:

Should I create an issue for this? @marco-c

Yes thanks!

Done here: https://github.com/mozilla/bugbug/issues/4356

benjaminmah avatar Jul 23 '24 16:07 benjaminmah

Hi! 🤖 The test below is automatically generated and could serve as a regression test for this PR because it:

  • passes, and
  • fails in the codebase before the PR.
def test_initial_product_and_component():
    import json
    from bugbug.bug_features import InitialProduct, InitialComponent

    # Sample bug data with history
    bug_with_history = {
        "product": "Firefox",
        "component": "General",
        "history": [
            {
                "changes": [
                    {"field_name": "product", "removed": "Core", "added": "Firefox"},
                    {"field_name": "component", "removed": "DOM", "added": "General"},
                ]
            }
        ],
    }

    # Sample bug data without history
    bug_without_history = {
        "product": "Toolkit",
        "component": "Password Manager",
        "history": [],
    }

    # Initialize feature extractors
    initial_product_extractor = InitialProduct()
    initial_component_extractor = InitialComponent()

    # Test with history
    assert initial_product_extractor(bug_with_history) == "Core"
    assert initial_component_extractor(bug_with_history) == "DOM"

    # Test without history
    assert initial_product_extractor(bug_without_history) == "Toolkit"
    assert initial_component_extractor(bug_without_history) == "Password Manager"

If you find this regression test useful, feel free to insert it to your test suite. Our automated pipeline inserted the test at the end of the tests/test_bug_features.py file before running it.

This is part of our research at the ZEST group of University of Zurich in collaboration with Mozilla. If you have any suggestions, questions, or simply want to learn more, feel free to contact us at [email protected] and [email protected].

Click to see which lines were covered.
diff --git a/bugbug/bug_features.py b/bugbug/bug_features.py
--- a/bugbug/bug_features.py
+++ b/bugbug/bug_features.py
@@ -900,6 +900,27 @@
         ]
 
 
+class InitialProduct(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if change["field_name"] == "product":# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return bug["product"]# ✅ Covered by above test
+# ✅ Covered by above test
+# ✅ Covered by above test
+class InitialComponent(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if change["field_name"] == "component":# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return bug["component"]# ✅ Covered by above test
+# ✅ Covered by above test
 class BugType(SingleBugFeature):
     """Extracts the type of the bug."""
 

diff --git a/bugbug/models/component.py b/bugbug/models/component.py
--- a/bugbug/models/component.py
+++ b/bugbug/models/component.py
@@ -84,6 +84,8 @@
             bug_features.Whiteboard(),
             bug_features.Patches(),
             bug_features.Landings(),
+            bug_features.InitialProduct(), 
+            bug_features.InitialComponent(), 
         ]
 
         cleanup_functions = [

Line coverage* achieved: 91.3%

* Line coverage is calculated over the lines added in this PR.

pr-test-bot avatar Apr 14 '25 20:04 pr-test-bot

Hi! 🤖 The test below is automatically generated and could serve as a regression test for this PR because it:

  • passes, and
  • fails in the codebase before the PR.
def test_initial_product_and_component():
    import json
    from bugbug.bug_features import InitialProduct, InitialComponent

    # Sample bug data with history
    bug_with_history = {
        "product": "Firefox",
        "component": "General",
        "history": [
            {
                "changes": [
                    {"field_name": "product", "removed": "Core"},
                    {"field_name": "component", "removed": "DOM"},
                ]
            }
        ],
    }

    # Sample bug data without history
    bug_without_history = {
        "product": "Firefox",
        "component": "General",
        "history": [],
    }

    # Initialize feature extractors
    initial_product_extractor = InitialProduct()
    initial_component_extractor = InitialComponent()

    # Test with history
    assert initial_product_extractor(bug_with_history) == "Core"
    assert initial_component_extractor(bug_with_history) == "DOM"

    # Test without history
    assert initial_product_extractor(bug_without_history) == "Firefox"
    assert initial_component_extractor(bug_without_history) == "General"

If you find this regression test useful, feel free to insert it to your test suite. Our automated pipeline inserted the test at the end of the tests/test_bug_features.py file before running it.

This is part of our research at the ZEST group of University of Zurich in collaboration with Mozilla. If you have any suggestions, questions, or simply want to learn more, feel free to contact us at [email protected] and [email protected].

Click to see which lines were covered.
diff --git a/bugbug/bug_features.py b/bugbug/bug_features.py
--- a/bugbug/bug_features.py
+++ b/bugbug/bug_features.py
@@ -905,3 +905,24 @@
 
     def __call__(self, bug, **kwargs):
         return bug["type"]
+# ✅ Covered by above test
+class InitialProduct(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if change["field_name"] == "product":# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return bug["product"]# ✅ Covered by above test
+# ✅ Covered by above test
+# ✅ Covered by above test
+class InitialComponent(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if change["field_name"] == "component":# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return bug["component"]# ✅ Covered by above test

diff --git a/bugbug/models/component.py b/bugbug/models/component.py
--- a/bugbug/models/component.py
+++ b/bugbug/models/component.py
@@ -84,6 +84,8 @@
             bug_features.Whiteboard(),
             bug_features.Patches(),
             bug_features.Landings(),
+            bug_features.InitialProduct(), 
+            bug_features.InitialComponent(), 
         ]
 
         cleanup_functions = [

Line coverage* achieved: 91.3%

* Line coverage is calculated over the lines added in this PR.

pr-test-bot avatar Apr 14 '25 20:04 pr-test-bot

With the feature (highest threshold): pre rec spe f1 geo iba sup avg / total 0.57 0.50 1.00 0.53 0.54 0.49 8986

Without the feature (highest threshold): pre rec spe f1 geo iba sup avg / total 0.51 0.28 1.00 0.35 0.39 0.26 8986

benjaminmah avatar Apr 14 '25 22:04 benjaminmah

Hi! 🤖 The test below is automatically generated and could serve as a regression test for this PR because it:

  • passes, and
  • fails in the codebase before the PR.
def test_initial_product_and_component():
    import json
    from bugbug.bug_features import InitialProduct, InitialComponent

    # Sample bug data with history
    bug_with_history = {
        "product": "Firefox",
        "component": "General",
        "history": [
            {
                "changes": [
                    {"field_name": "product", "removed": "Core", "added": "Firefox"},
                    {"field_name": "component", "removed": "DOM", "added": "General"},
                ]
            }
        ],
    }

    # Sample bug data without history
    bug_without_history = {
        "product": "Firefox",
        "component": "General",
        "history": [],
    }

    # Initialize feature extractors
    initial_product_extractor = InitialProduct()
    initial_component_extractor = InitialComponent()

    # Test with history
    assert initial_product_extractor(bug_with_history) == "Core"
    assert initial_component_extractor(bug_with_history) == "DOM"

    # Test without history
    assert initial_product_extractor(bug_without_history) == "Firefox"
    assert initial_component_extractor(bug_without_history) == "General"

If you find this regression test useful, feel free to insert it to your test suite. Our automated pipeline inserted the test at the end of the tests/test_bug_features.py file before running it.

This is part of our research at the ZEST group of University of Zurich in collaboration with Mozilla. If you have any suggestions, questions, or simply want to learn more, feel free to contact us at [email protected] and [email protected].

Click to see which lines were covered.
diff --git a/bugbug/bug_features.py b/bugbug/bug_features.py
--- a/bugbug/bug_features.py
+++ b/bugbug/bug_features.py
@@ -905,3 +905,25 @@
 
     def __call__(self, bug, **kwargs):
         return bug["type"]
+# ✅ Covered by above test
+# ✅ Covered by above test
+class InitialProduct(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if change["field_name"] == "product":# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return bug["product"]# ✅ Covered by above test
+# ✅ Covered by above test
+# ✅ Covered by above test
+class InitialComponent(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if change["field_name"] == "component":# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return bug["component"]# ✅ Covered by above test

diff --git a/bugbug/models/component.py b/bugbug/models/component.py
--- a/bugbug/models/component.py
+++ b/bugbug/models/component.py
@@ -84,6 +84,8 @@
             bug_features.Whiteboard(),
             bug_features.Patches(),
             bug_features.Landings(),
+            bug_features.InitialProduct(), 
+            bug_features.InitialComponent(), 
         ]
 
         cleanup_functions = [

Line coverage* achieved: 91.7%

* Line coverage is calculated over the lines added in this PR.

pr-test-bot avatar Apr 14 '25 22:04 pr-test-bot

With the feature (highest threshold): pre rec spe f1 geo iba sup avg / total 0.57 0.50 1.00 0.53 0.54 0.49 8986

Without the feature (highest threshold): pre rec spe f1 geo iba sup avg / total 0.51 0.28 1.00 0.35 0.39 0.26 8986

Correction, with the feature the results are pre rec spe f1 geo iba sup avg / total 0.53 0.29 1.00 0.36 0.40 0.28 8984

benjaminmah avatar Apr 15 '25 19:04 benjaminmah

Hi! 🤖 The test below is automatically generated and could serve as a regression test for this PR because it:

  • passes, and
  • fails in the codebase before the PR.
def test_initial_product_and_component():
    import json
    from bugbug.bug_features import InitialProduct, InitialComponent

    # Sample bug data with history of changes
    bug_data_with_history = {
        "history": [
            {
                "changes": [
                    {"field_name": "product", "removed": "Firefox"},
                    {"field_name": "component", "removed": "General"},
                ]
            }
        ]
    }

    # Sample bug data without history
    bug_data_without_history = {}

    # Initialize feature extractors
    initial_product_extractor = InitialProduct()
    initial_component_extractor = InitialComponent()

    # Test with history
    assert initial_product_extractor(bug_data_with_history) == "Firefox"
    assert initial_component_extractor(bug_data_with_history) == "General"

    # Test without history
    assert initial_product_extractor(bug_data_without_history) is None
    assert initial_component_extractor(bug_data_without_history) is None

If you find this regression test useful, feel free to insert it to your test suite. Our automated pipeline inserted the test at the end of the tests/test_bug_features.py file before running it.

This is part of our research at the ZEST group of University of Zurich in collaboration with Mozilla. If you have any suggestions, questions, or simply want to learn more, feel free to contact us at [email protected] and [email protected].

Click to see which lines were covered.
diff --git a/bugbug/bug_features.py b/bugbug/bug_features.py
--- a/bugbug/bug_features.py
+++ b/bugbug/bug_features.py
@@ -905,3 +905,25 @@
 
     def __call__(self, bug, **kwargs):
         return bug["type"]
+# ✅ Covered by above test
+# ✅ Covered by above test
+class InitialProduct(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if change["field_name"] == "product":# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return None# ✅ Covered by above test
+# ✅ Covered by above test
+# ✅ Covered by above test
+class InitialComponent(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if change["field_name"] == "component":# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return None# ✅ Covered by above test

diff --git a/bugbug/models/component.py b/bugbug/models/component.py
--- a/bugbug/models/component.py
+++ b/bugbug/models/component.py
@@ -84,6 +84,8 @@
             bug_features.Whiteboard(),
             bug_features.Patches(),
             bug_features.Landings(),
+            bug_features.InitialProduct(), 
+            bug_features.InitialComponent(), 
         ]
 
         cleanup_functions = [

Line coverage* achieved: 91.7%

* Line coverage is calculated over the lines added in this PR.

pr-test-bot avatar Apr 15 '25 19:04 pr-test-bot

Hi! 🤖 The test below is automatically generated and could serve as a regression test for this PR because it:

  • passes, and
  • fails in the codebase before the PR.
def test_initial_product_and_component():
    import json
    from bugbug.bug_features import InitialProduct, InitialComponent

    # Sample bug data with history
    bug_with_history = {
        "product": "Firefox",
        "component": "General",
        "history": [
            {
                "changes": [
                    {"field_name": "product", "removed": "Core", "added": "Firefox"},
                    {"field_name": "component", "removed": "Networking", "added": "General"},
                ]
            }
        ],
    }

    # Sample bug data without history
    bug_without_history = {
        "product": "Firefox",
        "component": "General",
        "history": [],
    }

    # Initialize feature extractors
    initial_product_extractor = InitialProduct()
    initial_component_extractor = InitialComponent()

    # Test cases
    assert initial_product_extractor(bug_with_history) == "Core"
    assert initial_component_extractor(bug_with_history) == "Networking"
    assert initial_product_extractor(bug_without_history) is None
    assert initial_component_extractor(bug_without_history) is None

If you find this regression test useful, feel free to insert it to your test suite. Our automated pipeline inserted the test at the end of the tests/test_bug_features.py file before running it.

This is part of our research at the ZEST group of University of Zurich in collaboration with Mozilla. If you have any suggestions, questions, or simply want to learn more, feel free to contact us at [email protected] and [email protected].

Click to see which lines were covered.
diff --git a/bugbug/bug_features.py b/bugbug/bug_features.py
--- a/bugbug/bug_features.py
+++ b/bugbug/bug_features.py
@@ -905,3 +905,31 @@
 
     def __call__(self, bug, **kwargs):
         return bug["type"]
+# ✅ Covered by above test
+# ✅ Covered by above test
+class InitialProduct(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if (# ✅ Covered by above test
+                        change["field_name"] == "product"# ✅ Covered by above test
+                        and change["removed"] != bug["product"]# ✅ Covered by above test
+                    ):# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return None# ✅ Covered by above test
+# ✅ Covered by above test
+# ✅ Covered by above test
+class InitialComponent(SingleBugFeature):# ✅ Covered by above test
+    def __call__(self, bug, **kwargs):# ✅ Covered by above test
+        history = bug.get("history", [])# ✅ Covered by above test
+        if history:# ✅ Covered by above test
+            for entry in history:# ✅ Covered by above test
+                for change in entry["changes"]:# ✅ Covered by above test
+                    if (# ✅ Covered by above test
+                        change["field_name"] == "component"# ✅ Covered by above test
+                        and change["removed"] != bug["component"]# ✅ Covered by above test
+                    ):# ✅ Covered by above test
+                        return change["removed"]# ✅ Covered by above test
+        return None# ✅ Covered by above test

diff --git a/bugbug/models/component.py b/bugbug/models/component.py
--- a/bugbug/models/component.py
+++ b/bugbug/models/component.py
@@ -84,6 +84,8 @@
             bug_features.Whiteboard(),
             bug_features.Patches(),
             bug_features.Landings(),
+            bug_features.InitialProduct(), 
+            bug_features.InitialComponent(), 
         ]
 
         cleanup_functions = [

Line coverage* achieved: 93.3%

* Line coverage is calculated over the lines added in this PR.

pr-test-bot avatar Apr 15 '25 19:04 pr-test-bot

Closing due to https://github.com/mozilla/bugbug/pull/4298#discussion_r2045813624

suhaibmujahid avatar Apr 16 '25 02:04 suhaibmujahid