Build a summary from a list of per-file option counts. Empty input yields :meth:`empty` rather than ``None`` — the field is always present in the report.
(cls, counts: list[int])
| 88 | |
| 89 | @classmethod |
| 90 | def from_counts(cls, counts: list[int]) -> OptionCountSummary: |
| 91 | """Build a summary from a list of per-file option counts. |
| 92 | |
| 93 | Empty input yields :meth:`empty` rather than ``None`` — the field |
| 94 | is always present in the report. |
| 95 | """ |
| 96 | if not counts: |
| 97 | return cls.empty() |
| 98 | buckets = {"0": 0, "1-5": 0, "6-15": 0, "16-50": 0, "50+": 0} |
| 99 | for c in counts: |
| 100 | if c == 0: |
| 101 | buckets["0"] += 1 |
| 102 | elif c <= 5: |
| 103 | buckets["1-5"] += 1 |
| 104 | elif c <= 15: |
| 105 | buckets["6-15"] += 1 |
| 106 | elif c <= 50: |
| 107 | buckets["16-50"] += 1 |
| 108 | else: |
| 109 | buckets["50+"] += 1 |
| 110 | # statistics.quantiles requires n >= 2 data points. |
| 111 | if len(counts) >= 2: |
| 112 | p90 = statistics.quantiles(counts, n=10)[8] |
| 113 | else: |
| 114 | p90 = float(counts[0]) |
| 115 | return cls( |
| 116 | n=len(counts), |
| 117 | total=sum(counts), |
| 118 | mean=round(statistics.mean(counts), 2), |
| 119 | median=float(statistics.median(counts)), |
| 120 | p90=round(float(p90), 2), |
| 121 | max=max(counts), |
| 122 | buckets=buckets, |
| 123 | ) |
| 124 | |
| 125 | |
| 126 | class FailureEntry(BaseModel): |