refactor: move duplicate code into methods

GitOrigin-RevId: e47e7e758bfc48537f0f21d40cef8e5fa2a076c6
Change-Id: I413a0865c2bc6d24951048d74019ddb1ea62881c
diff --git a/coverage/jsonreport.py b/coverage/jsonreport.py
index 3db266b..1b9b42e 100644
--- a/coverage/jsonreport.py
+++ b/coverage/jsonreport.py
@@ -9,7 +9,7 @@
 import json
 import sys
 
-from typing import Any, IO, Iterable, TYPE_CHECKING
+from typing import Any, Dict, IO, Iterable, TYPE_CHECKING
 
 from coverage import __version__
 from coverage.report_core import get_analysis_to_report
@@ -22,6 +22,9 @@
     from coverage.plugin import FileReporter
 
 
+# A type for data that can be JSON-serialized.
+JsonObj = Dict[str, Any]
+
 # "Version 1" had no format number at all.
 # 2: add the meta.format field.
 # 3: add region information (functions, classes)
@@ -36,7 +39,27 @@
         self.coverage = coverage
         self.config = self.coverage.config
         self.total = Numbers(self.config.precision)
-        self.report_data: dict[str, Any] = {}
+        self.report_data: JsonObj = {}
+
+    def make_summary(self, nums: Numbers) -> JsonObj:
+        """Create a dict summarizing `nums`."""
+        return {
+            "covered_lines": nums.n_executed,
+            "num_statements": nums.n_statements,
+            "percent_covered": nums.pc_covered,
+            "percent_covered_display": nums.pc_covered_str,
+            "missing_lines": nums.n_missing,
+            "excluded_lines": nums.n_excluded,
+        }
+
+    def make_branch_summary(self, nums: Numbers) -> JsonObj:
+        """Create a dict summarizing the branch info in `nums`."""
+        return {
+            "num_branches": nums.n_branches,
+            "num_partial_branches": nums.n_partial_branches,
+            "covered_branches": nums.n_executed_branches,
+            "missing_branches": nums.n_missing_branches,
+        }
 
     def report(self, morfs: Iterable[TMorf] | None, outfile: IO[str]) -> float:
         """Generate a json report for `morfs`.
@@ -66,23 +89,10 @@
             )
 
         self.report_data["files"] = measured_files
-
-        self.report_data["totals"] = {
-            "covered_lines": self.total.n_executed,
-            "num_statements": self.total.n_statements,
-            "percent_covered": self.total.pc_covered,
-            "percent_covered_display": self.total.pc_covered_str,
-            "missing_lines": self.total.n_missing,
-            "excluded_lines": self.total.n_excluded,
-        }
+        self.report_data["totals"] = self.make_summary(self.total)
 
         if coverage_data.has_arcs():
-            self.report_data["totals"].update({
-                "num_branches": self.total.n_branches,
-                "num_partial_branches": self.total.n_partial_branches,
-                "covered_branches": self.total.n_executed_branches,
-                "missing_branches": self.total.n_missing_branches,
-            })
+            self.report_data["totals"].update(self.make_branch_summary(self.total))
 
         json.dump(
             self.report_data,
@@ -94,19 +104,12 @@
 
     def report_one_file(
         self, coverage_data: CoverageData, analysis: Analysis, file_reporter: FileReporter
-    ) -> dict[str, Any]:
+    ) -> JsonObj:
         """Extract the relevant report data for a single file."""
         nums = analysis.numbers
         self.total += nums
-        summary = {
-            "covered_lines": nums.n_executed,
-            "num_statements": nums.n_statements,
-            "percent_covered": nums.pc_covered,
-            "percent_covered_display": nums.pc_covered_str,
-            "missing_lines": nums.n_missing,
-            "excluded_lines": nums.n_excluded,
-        }
-        reported_file: dict[str, Any] = {
+        summary = self.make_summary(nums)
+        reported_file: JsonObj = {
             "executed_lines": sorted(analysis.executed),
             "summary": summary,
             "missing_lines": sorted(analysis.missing),
@@ -115,12 +118,7 @@
         if self.config.json_show_contexts:
             reported_file["contexts"] = coverage_data.contexts_by_lineno(analysis.filename)
         if coverage_data.has_arcs():
-            summary.update({
-                "num_branches": nums.n_branches,
-                "num_partial_branches": nums.n_partial_branches,
-                "covered_branches": nums.n_executed_branches,
-                "missing_branches": nums.n_missing_branches,
-            })
+            summary.update(self.make_branch_summary(nums))
             reported_file["executed_branches"] = list(
                 _convert_branch_arcs(analysis.executed_branch_arcs()),
             )
@@ -136,14 +134,7 @@
             outside_lines -= region.lines
             narrowed_analysis = analysis.narrow(region.lines)
             narrowed_nums = narrowed_analysis.numbers
-            narrowed_summary = {
-                "covered_lines": narrowed_nums.n_executed,
-                "num_statements": narrowed_nums.n_statements,
-                "percent_covered": narrowed_nums.pc_covered,
-                "percent_covered_display": narrowed_nums.pc_covered_str,
-                "missing_lines": narrowed_nums.n_missing,
-                "excluded_lines": narrowed_nums.n_excluded,
-            }
+            narrowed_summary = self.make_summary(narrowed_nums)
             reported_file[region.kind][region.name] = {
                 "executed_lines": sorted(narrowed_analysis.executed),
                 "summary": narrowed_summary,
@@ -154,18 +145,14 @@
                 contexts = coverage_data.contexts_by_lineno(narrowed_analysis.filename)
                 reported_file[region.kind][region.name]["contexts"] = contexts
             if coverage_data.has_arcs():
-                narrowed_summary.update({
-                    "num_branches": narrowed_nums.n_branches,
-                    "num_partial_branches": narrowed_nums.n_partial_branches,
-                    "covered_branches": narrowed_nums.n_executed_branches,
-                    "missing_branches": narrowed_nums.n_missing_branches,
-                })
+                narrowed_summary.update(self.make_branch_summary(narrowed_nums))
                 reported_file[region.kind][region.name]["executed_branches"] = list(
                     _convert_branch_arcs(narrowed_analysis.executed_branch_arcs()),
                 )
                 reported_file[region.kind][region.name]["missing_branches"] = list(
                     _convert_branch_arcs(narrowed_analysis.missing_branch_arcs()),
                 )
+
         return reported_file