We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Optimize the `bug_to_types()` functions to avoid redundant the execution of string lowering
The `bug["whiteboard"].lower()` statement is performed multiple times in the function. Instead, we could store the lowercase value in a variable (e.g., `bug_whiteboard`) and reuse it where needed.

Examples:

https://github.com/mozilla/bugbug/blob/0586c6a49732f03781daf43d8726b53e2049e3db/bugbug/models/bugtype.py#L46

https://github.com/mozilla/bugbug/blob/0586c6a49732f03781daf43d8726b53e2049e3db/bugbug/models/bugtype.py#L50


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bugbug/models/bugtype.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 # You can obtain one at http://mozilla.org/MPL/2.0/.
5 
6 import logging
7 from typing import Iterable, Optional
8 
9 import numpy as np
10 import xgboost
11 from sklearn.compose import ColumnTransformer
12 from sklearn.feature_extraction import DictVectorizer
13 from sklearn.multiclass import OneVsRestClassifier
14 from sklearn.pipeline import Pipeline
15 
16 from bugbug import bug_features, bugzilla, feature_cleanup, utils
17 from bugbug.model import BugModel
18 
19 logger = logging.getLogger(__name__)
20 
21 KEYWORD_DICT = {
22     "sec-": "security",
23     "csectype-": "security",
24     "memory-": "memory",
25     "crash": "crash",
26     "crashreportid": "crash",
27     "perf": "performance",
28     "topperf": "performance",
29     "main-thread-io": "performance",
30     "power": "power",
31 }
32 TYPE_LIST = sorted(set(KEYWORD_DICT.values()))
33 
34 
35 def bug_to_types(
36     bug: bugzilla.BugDict, bug_map: Optional[dict[int, bugzilla.BugDict]] = None
37 ) -> list[str]:
38     types = set()
39 
40     bug_whiteboard = bug["whiteboard"].lower()
41 
42     if any(
43         f"{whiteboard_text}" in bug_whiteboard
44         for whiteboard_text in ("overhead", "memshrink")
45     ):
46         types.add("memory")
47 
48     if "[power" in bug_whiteboard:
49         types.add("power")
50 
51     if any(
52         f"[{whiteboard_text}" in bug_whiteboard
53         for whiteboard_text in (
54             "fxperf",
55             "fxperfsize",
56             "snappy",
57             "pdfjs-c-performance",
58             "pdfjs-performance",
59             "sp3",
60         )
61     ):
62         types.add("performance")
63 
64     if any(
65         f"[{whiteboard_text}" in bug["whiteboard"].lower()
66         for whiteboard_text in ("client-bounty-form", "sec-survey")
67     ):
68         types.add("security")
69 
70     if "cf_performance" in bug and bug["cf_performance"] not in ("---", "?"):
71         types.add("performance")
72 
73     if "cf_crash_signature" in bug and bug["cf_crash_signature"] not in ("", "---"):
74         types.add("crash")
75 
76     if bug_map is not None:
77         for bug_id in bug["blocks"]:
78             if bug_id not in bug_map:
79                 continue
80 
81             alias = bug_map[bug_id]["alias"]
82             if alias and alias.startswith("memshrink"):
83                 types.add("memory")
84 
85     for keyword_start, type in KEYWORD_DICT.items():
86         if any(keyword.startswith(keyword_start) for keyword in bug["keywords"]):
87             types.add(type)
88 
89     return list(types)
90 
91 
92 class BugTypeModel(BugModel):
93     def __init__(self, lemmatization=False, historical=False):
94         BugModel.__init__(self, lemmatization)
95 
96         self.calculate_importance = False
97 
98         feature_extractors = [
99             bug_features.HasSTR(),
100             bug_features.Severity(),
101             # Ignore keywords that would make the ML completely skewed
102             # (we are going to use them as 100% rules in the evaluation phase).
103             bug_features.Keywords(set(KEYWORD_DICT.keys())),
104             bug_features.IsCoverityIssue(),
105             bug_features.HasCrashSignature(),
106             bug_features.HasURL(),
107             bug_features.HasW3CURL(),
108             bug_features.HasGithubURL(),
109             bug_features.Whiteboard(),
110             bug_features.Patches(),
111             bug_features.Landings(),
112             bug_features.BlockedBugsNumber(),
113             bug_features.EverAffected(),
114             bug_features.AffectedThenUnaffected(),
115             bug_features.Product(),
116             bug_features.Component(),
117         ]
118 
119         cleanup_functions = [
120             feature_cleanup.url(),
121             feature_cleanup.fileref(),
122             feature_cleanup.synonyms(),
123         ]
124 
125         self.extraction_pipeline = Pipeline(
126             [
127                 (
128                     "bug_extractor",
129                     bug_features.BugExtractor(feature_extractors, cleanup_functions),
130                 ),
131                 (
132                     "union",
133                     ColumnTransformer(
134                         [
135                             ("data", DictVectorizer(), "data"),
136                             ("title", self.text_vectorizer(min_df=0.001), "title"),
137                             (
138                                 "first_comment",
139                                 self.text_vectorizer(min_df=0.001),
140                                 "first_comment",
141                             ),
142                             (
143                                 "comments",
144                                 self.text_vectorizer(min_df=0.001),
145                                 "comments",
146                             ),
147                         ]
148                     ),
149                 ),
150             ]
151         )
152 
153         self.hyperparameter = {"n_jobs": utils.get_physical_cpu_count()}
154         self.clf = OneVsRestClassifier(xgboost.XGBClassifier(**self.hyperparameter))
155 
156     def get_labels(self) -> tuple[dict[int, np.ndarray], list[str]]:
157         classes = {}
158 
159         bug_map = {bug["id"]: bug for bug in bugzilla.get_bugs()}
160 
161         for bug_data in bug_map.values():
162             target = np.zeros(len(TYPE_LIST))
163             for type_ in bug_to_types(bug_data, bug_map):
164                 target[TYPE_LIST.index(type_)] = 1
165 
166             classes[int(bug_data["id"])] = target
167 
168         for type_ in TYPE_LIST:
169             logger.info(
170                 "%d %s bugs",
171                 sum(
172                     1
173                     for target in classes.values()
174                     if target[TYPE_LIST.index(type_)] == 1
175                 ),
176                 type_,
177             )
178 
179         return classes, TYPE_LIST
180 
181     def get_feature_names(self):
182         return self.extraction_pipeline.named_steps["union"].get_feature_names_out()
183 
184     def overwrite_classes(
185         self,
186         bugs: Iterable[bugzilla.BugDict],
187         classes: dict[int, np.ndarray],
188         probabilities: bool,
189     ):
190         for i, bug in enumerate(bugs):
191             for type_ in bug_to_types(bug):
192                 if probabilities:
193                     classes[i][TYPE_LIST.index(type_)] = 1.0
194                 else:
195                     classes[i][TYPE_LIST.index(type_)] = 1
196 
197         return classes
198
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bugbug/models/bugtype.py b/bugbug/models/bugtype.py
--- a/bugbug/models/bugtype.py
+++ b/bugbug/models/bugtype.py
@@ -62,7 +62,7 @@
         types.add("performance")
 
     if any(
-        f"[{whiteboard_text}" in bug["whiteboard"].lower()
+        f"[{whiteboard_text}" in bug_whiteboard
         for whiteboard_text in ("client-bounty-form", "sec-survey")
     ):
         types.add("security")

{"golden_diff": "diff --git a/bugbug/models/bugtype.py b/bugbug/models/bugtype.py\n--- a/bugbug/models/bugtype.py\n+++ b/bugbug/models/bugtype.py\n@@ -62,7 +62,7 @@\n         types.add(\"performance\")\n \n     if any(\n-        f\"[{whiteboard_text}\" in bug[\"whiteboard\"].lower()\n+        f\"[{whiteboard_text}\" in bug_whiteboard\n         for whiteboard_text in (\"client-bounty-form\", \"sec-survey\")\n     ):\n         types.add(\"security\")\n", "issue": "Optimize the `bug_to_types()` functions to avoid redundant the execution of string lowering\nThe `bug[\"whiteboard\"].lower()` statement is performed multiple times in the function. Instead, we could store the lowercase value in a variable (e.g., `bug_whiteboard`) and reuse it where needed.\r\n\r\nExamples:\r\n\r\nhttps://github.com/mozilla/bugbug/blob/0586c6a49732f03781daf43d8726b53e2049e3db/bugbug/models/bugtype.py#L46\r\n\r\nhttps://github.com/mozilla/bugbug/blob/0586c6a49732f03781daf43d8726b53e2049e3db/bugbug/models/bugtype.py#L50\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this file,\n# You can obtain one at http://mozilla.org/MPL/2.0/.\n\nimport logging\nfrom typing import Iterable, Optional\n\nimport numpy as np\nimport xgboost\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.feature_extraction import DictVectorizer\nfrom sklearn.multiclass import OneVsRestClassifier\nfrom sklearn.pipeline import Pipeline\n\nfrom bugbug import bug_features, bugzilla, feature_cleanup, utils\nfrom bugbug.model import BugModel\n\nlogger = logging.getLogger(__name__)\n\nKEYWORD_DICT = {\n    \"sec-\": \"security\",\n    \"csectype-\": \"security\",\n    \"memory-\": \"memory\",\n    \"crash\": \"crash\",\n    \"crashreportid\": \"crash\",\n    \"perf\": \"performance\",\n    \"topperf\": \"performance\",\n    \"main-thread-io\": \"performance\",\n    \"power\": \"power\",\n}\nTYPE_LIST = sorted(set(KEYWORD_DICT.values()))\n\n\ndef bug_to_types(\n    bug: bugzilla.BugDict, bug_map: Optional[dict[int, bugzilla.BugDict]] = None\n) -> list[str]:\n    types = set()\n\n    bug_whiteboard = bug[\"whiteboard\"].lower()\n\n    if any(\n        f\"{whiteboard_text}\" in bug_whiteboard\n        for whiteboard_text in (\"overhead\", \"memshrink\")\n    ):\n        types.add(\"memory\")\n\n    if \"[power\" in bug_whiteboard:\n        types.add(\"power\")\n\n    if any(\n        f\"[{whiteboard_text}\" in bug_whiteboard\n        for whiteboard_text in (\n            \"fxperf\",\n            \"fxperfsize\",\n            \"snappy\",\n            \"pdfjs-c-performance\",\n            \"pdfjs-performance\",\n            \"sp3\",\n        )\n    ):\n        types.add(\"performance\")\n\n    if any(\n        f\"[{whiteboard_text}\" in bug[\"whiteboard\"].lower()\n        for whiteboard_text in (\"client-bounty-form\", \"sec-survey\")\n    ):\n        types.add(\"security\")\n\n    if \"cf_performance\" in bug and bug[\"cf_performance\"] not in (\"---\", \"?\"):\n        types.add(\"performance\")\n\n    if \"cf_crash_signature\" in bug and bug[\"cf_crash_signature\"] not in (\"\", \"---\"):\n        types.add(\"crash\")\n\n    if bug_map is not None:\n        for bug_id in bug[\"blocks\"]:\n            if bug_id not in bug_map:\n                continue\n\n            alias = bug_map[bug_id][\"alias\"]\n            if alias and alias.startswith(\"memshrink\"):\n                types.add(\"memory\")\n\n    for keyword_start, type in KEYWORD_DICT.items():\n        if any(keyword.startswith(keyword_start) for keyword in bug[\"keywords\"]):\n            types.add(type)\n\n    return list(types)\n\n\nclass BugTypeModel(BugModel):\n    def __init__(self, lemmatization=False, historical=False):\n        BugModel.__init__(self, lemmatization)\n\n        self.calculate_importance = False\n\n        feature_extractors = [\n            bug_features.HasSTR(),\n            bug_features.Severity(),\n            # Ignore keywords that would make the ML completely skewed\n            # (we are going to use them as 100% rules in the evaluation phase).\n            bug_features.Keywords(set(KEYWORD_DICT.keys())),\n            bug_features.IsCoverityIssue(),\n            bug_features.HasCrashSignature(),\n            bug_features.HasURL(),\n            bug_features.HasW3CURL(),\n            bug_features.HasGithubURL(),\n            bug_features.Whiteboard(),\n            bug_features.Patches(),\n            bug_features.Landings(),\n            bug_features.BlockedBugsNumber(),\n            bug_features.EverAffected(),\n            bug_features.AffectedThenUnaffected(),\n            bug_features.Product(),\n            bug_features.Component(),\n        ]\n\n        cleanup_functions = [\n            feature_cleanup.url(),\n            feature_cleanup.fileref(),\n            feature_cleanup.synonyms(),\n        ]\n\n        self.extraction_pipeline = Pipeline(\n            [\n                (\n                    \"bug_extractor\",\n                    bug_features.BugExtractor(feature_extractors, cleanup_functions),\n                ),\n                (\n                    \"union\",\n                    ColumnTransformer(\n                        [\n                            (\"data\", DictVectorizer(), \"data\"),\n                            (\"title\", self.text_vectorizer(min_df=0.001), \"title\"),\n                            (\n                                \"first_comment\",\n                                self.text_vectorizer(min_df=0.001),\n                                \"first_comment\",\n                            ),\n                            (\n                                \"comments\",\n                                self.text_vectorizer(min_df=0.001),\n                                \"comments\",\n                            ),\n                        ]\n                    ),\n                ),\n            ]\n        )\n\n        self.hyperparameter = {\"n_jobs\": utils.get_physical_cpu_count()}\n        self.clf = OneVsRestClassifier(xgboost.XGBClassifier(**self.hyperparameter))\n\n    def get_labels(self) -> tuple[dict[int, np.ndarray], list[str]]:\n        classes = {}\n\n        bug_map = {bug[\"id\"]: bug for bug in bugzilla.get_bugs()}\n\n        for bug_data in bug_map.values():\n            target = np.zeros(len(TYPE_LIST))\n            for type_ in bug_to_types(bug_data, bug_map):\n                target[TYPE_LIST.index(type_)] = 1\n\n            classes[int(bug_data[\"id\"])] = target\n\n        for type_ in TYPE_LIST:\n            logger.info(\n                \"%d %s bugs\",\n                sum(\n                    1\n                    for target in classes.values()\n                    if target[TYPE_LIST.index(type_)] == 1\n                ),\n                type_,\n            )\n\n        return classes, TYPE_LIST\n\n    def get_feature_names(self):\n        return self.extraction_pipeline.named_steps[\"union\"].get_feature_names_out()\n\n    def overwrite_classes(\n        self,\n        bugs: Iterable[bugzilla.BugDict],\n        classes: dict[int, np.ndarray],\n        probabilities: bool,\n    ):\n        for i, bug in enumerate(bugs):\n            for type_ in bug_to_types(bug):\n                if probabilities:\n                    classes[i][TYPE_LIST.index(type_)] = 1.0\n                else:\n                    classes[i][TYPE_LIST.index(type_)] = 1\n\n        return classes\n", "path": "bugbug/models/bugtype.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this file,\n# You can obtain one at http://mozilla.org/MPL/2.0/.\n\nimport logging\nfrom typing import Iterable, Optional\n\nimport numpy as np\nimport xgboost\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.feature_extraction import DictVectorizer\nfrom sklearn.multiclass import OneVsRestClassifier\nfrom sklearn.pipeline import Pipeline\n\nfrom bugbug import bug_features, bugzilla, feature_cleanup, utils\nfrom bugbug.model import BugModel\n\nlogger = logging.getLogger(__name__)\n\nKEYWORD_DICT = {\n    \"sec-\": \"security\",\n    \"csectype-\": \"security\",\n    \"memory-\": \"memory\",\n    \"crash\": \"crash\",\n    \"crashreportid\": \"crash\",\n    \"perf\": \"performance\",\n    \"topperf\": \"performance\",\n    \"main-thread-io\": \"performance\",\n    \"power\": \"power\",\n}\nTYPE_LIST = sorted(set(KEYWORD_DICT.values()))\n\n\ndef bug_to_types(\n    bug: bugzilla.BugDict, bug_map: Optional[dict[int, bugzilla.BugDict]] = None\n) -> list[str]:\n    types = set()\n\n    bug_whiteboard = bug[\"whiteboard\"].lower()\n\n    if any(\n        f\"{whiteboard_text}\" in bug_whiteboard\n        for whiteboard_text in (\"overhead\", \"memshrink\")\n    ):\n        types.add(\"memory\")\n\n    if \"[power\" in bug_whiteboard:\n        types.add(\"power\")\n\n    if any(\n        f\"[{whiteboard_text}\" in bug_whiteboard\n        for whiteboard_text in (\n            \"fxperf\",\n            \"fxperfsize\",\n            \"snappy\",\n            \"pdfjs-c-performance\",\n            \"pdfjs-performance\",\n            \"sp3\",\n        )\n    ):\n        types.add(\"performance\")\n\n    if any(\n        f\"[{whiteboard_text}\" in bug_whiteboard\n        for whiteboard_text in (\"client-bounty-form\", \"sec-survey\")\n    ):\n        types.add(\"security\")\n\n    if \"cf_performance\" in bug and bug[\"cf_performance\"] not in (\"---\", \"?\"):\n        types.add(\"performance\")\n\n    if \"cf_crash_signature\" in bug and bug[\"cf_crash_signature\"] not in (\"\", \"---\"):\n        types.add(\"crash\")\n\n    if bug_map is not None:\n        for bug_id in bug[\"blocks\"]:\n            if bug_id not in bug_map:\n                continue\n\n            alias = bug_map[bug_id][\"alias\"]\n            if alias and alias.startswith(\"memshrink\"):\n                types.add(\"memory\")\n\n    for keyword_start, type in KEYWORD_DICT.items():\n        if any(keyword.startswith(keyword_start) for keyword in bug[\"keywords\"]):\n            types.add(type)\n\n    return list(types)\n\n\nclass BugTypeModel(BugModel):\n    def __init__(self, lemmatization=False, historical=False):\n        BugModel.__init__(self, lemmatization)\n\n        self.calculate_importance = False\n\n        feature_extractors = [\n            bug_features.HasSTR(),\n            bug_features.Severity(),\n            # Ignore keywords that would make the ML completely skewed\n            # (we are going to use them as 100% rules in the evaluation phase).\n            bug_features.Keywords(set(KEYWORD_DICT.keys())),\n            bug_features.IsCoverityIssue(),\n            bug_features.HasCrashSignature(),\n            bug_features.HasURL(),\n            bug_features.HasW3CURL(),\n            bug_features.HasGithubURL(),\n            bug_features.Whiteboard(),\n            bug_features.Patches(),\n            bug_features.Landings(),\n            bug_features.BlockedBugsNumber(),\n            bug_features.EverAffected(),\n            bug_features.AffectedThenUnaffected(),\n            bug_features.Product(),\n            bug_features.Component(),\n        ]\n\n        cleanup_functions = [\n            feature_cleanup.url(),\n            feature_cleanup.fileref(),\n            feature_cleanup.synonyms(),\n        ]\n\n        self.extraction_pipeline = Pipeline(\n            [\n                (\n                    \"bug_extractor\",\n                    bug_features.BugExtractor(feature_extractors, cleanup_functions),\n                ),\n                (\n                    \"union\",\n                    ColumnTransformer(\n                        [\n                            (\"data\", DictVectorizer(), \"data\"),\n                            (\"title\", self.text_vectorizer(min_df=0.001), \"title\"),\n                            (\n                                \"first_comment\",\n                                self.text_vectorizer(min_df=0.001),\n                                \"first_comment\",\n                            ),\n                            (\n                                \"comments\",\n                                self.text_vectorizer(min_df=0.001),\n                                \"comments\",\n                            ),\n                        ]\n                    ),\n                ),\n            ]\n        )\n\n        self.hyperparameter = {\"n_jobs\": utils.get_physical_cpu_count()}\n        self.clf = OneVsRestClassifier(xgboost.XGBClassifier(**self.hyperparameter))\n\n    def get_labels(self) -> tuple[dict[int, np.ndarray], list[str]]:\n        classes = {}\n\n        bug_map = {bug[\"id\"]: bug for bug in bugzilla.get_bugs()}\n\n        for bug_data in bug_map.values():\n            target = np.zeros(len(TYPE_LIST))\n            for type_ in bug_to_types(bug_data, bug_map):\n                target[TYPE_LIST.index(type_)] = 1\n\n            classes[int(bug_data[\"id\"])] = target\n\n        for type_ in TYPE_LIST:\n            logger.info(\n                \"%d %s bugs\",\n                sum(\n                    1\n                    for target in classes.values()\n                    if target[TYPE_LIST.index(type_)] == 1\n                ),\n                type_,\n            )\n\n        return classes, TYPE_LIST\n\n    def get_feature_names(self):\n        return self.extraction_pipeline.named_steps[\"union\"].get_feature_names_out()\n\n    def overwrite_classes(\n        self,\n        bugs: Iterable[bugzilla.BugDict],\n        classes: dict[int, np.ndarray],\n        probabilities: bool,\n    ):\n        for i, bug in enumerate(bugs):\n            for type_ in bug_to_types(bug):\n                if probabilities:\n                    classes[i][TYPE_LIST.index(type_)] = 1.0\n                else:\n                    classes[i][TYPE_LIST.index(type_)] = 1\n\n        return classes\n", "path": "bugbug/models/bugtype.py"}]}