Skip to content

Commit

Permalink
Round the coverage numbers before saving to database (#447)
Browse files Browse the repository at this point in the history
  • Loading branch information
michelletran-codecov authored May 16, 2024
1 parent 0862b20 commit 07653e1
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 14 deletions.
12 changes: 10 additions & 2 deletions database/models/reports.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import uuid
from decimal import Decimal
from functools import cached_property

from shared.reports.types import ReportTotals, SessionTotalsArray
Expand All @@ -13,6 +14,7 @@
from database.utils import ArchiveField
from helpers.clock import get_utc_now
from helpers.config import should_write_data_to_storage_config_check
from helpers.number import precise_round

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -196,10 +198,16 @@ class AbstractTotals(MixinBaseClass):
partials = Column(types.Integer)
files = Column(types.Integer)

def update_from_totals(self, totals):
def update_from_totals(self, totals, precision=2, rounding="down"):
self.branches = totals.branches
if totals.coverage is not None:
coverage: Decimal = Decimal(totals.coverage)
self.coverage = precise_round(
coverage, precision=precision, rounding=rounding
)
# Temporary until the table starts accepting NULLs
self.coverage = totals.coverage if totals.coverage is not None else 0
else:
self.coverage = 0
self.hits = totals.hits
self.lines = totals.lines
self.methods = totals.methods
Expand Down
19 changes: 19 additions & 0 deletions helpers/number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from decimal import ROUND_CEILING, ROUND_FLOOR, ROUND_HALF_EVEN, Decimal


def precise_round(
number: Decimal, precision: int = 2, rounding: str = "down"
) -> Decimal:
"""
Helper function to do more precise rounding given a precision and rounding strategy.
:param number: Number to round
:param precision: The number of decimal places to round to
:param rounding: Rounding strategy to use, which can be "down", "up" or "nearest"
:return: The rounded number as a Decimal object
"""
quantizer = Decimal("0.1") ** precision
if rounding == "up":
return number.quantize(quantizer, rounding=ROUND_CEILING)
if rounding == "down":
return number.quantize(quantizer, rounding=ROUND_FLOOR)
return number.quantize(quantizer, rounding=ROUND_HALF_EVEN)
32 changes: 32 additions & 0 deletions helpers/tests/unit/test_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from decimal import Decimal

import pytest

from helpers.number import precise_round


@pytest.mark.parametrize(
"number,precision,rounding,expected_rounding",
[
(Decimal("1.129"), 2, "down", Decimal("1.12")),
(Decimal("1.121"), 2, "up", Decimal("1.13")),
(Decimal("1.125"), 1, "nearest", Decimal("1.1")),
(Decimal("1.18"), 1, "nearest", Decimal("1.2")),
(Decimal("1.15"), 1, "nearest", Decimal("1.2")),
(Decimal("1.25"), 1, "nearest", Decimal("1.2")),
],
ids=[
"number rounds down",
"number rounds up",
"number rounds nearest (down)",
"number rounds nearest (up)",
"number rounds half-even (up)",
"number rounds half-even (down)",
],
)
def test_precise_round(
number: Decimal, precision: int, rounding: str, expected_rounding: Decimal
):
assert expected_rounding == precise_round(
number, precision=precision, rounding=rounding
)
33 changes: 29 additions & 4 deletions services/report/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
from services.report.parser.types import ParsedRawReport
from services.report.raw_upload_processor import process_raw_upload
from services.repository import get_repo_provider_service
from services.yaml.reader import get_paths_from_flags
from services.yaml.reader import get_paths_from_flags, read_yaml_field


@dataclass
Expand Down Expand Up @@ -1050,6 +1050,12 @@ def build_report_from_raw_content(
def update_upload_with_processing_result(
self, upload_obj: Upload, processing_result: ProcessingResult
):
rounding: str = read_yaml_field(
self.current_yaml, ("coverage", "round"), "nearest"
)
precision: int = read_yaml_field(
self.current_yaml, ("coverage", "precision"), 2
)
db_session = upload_obj.get_db_session()
session = processing_result.session
if processing_result.error is None:
Expand Down Expand Up @@ -1078,7 +1084,9 @@ def update_upload_with_processing_result(
)
db_session.add(upload_totals)
if session.totals is not None:
upload_totals.update_from_totals(session.totals)
upload_totals.update_from_totals(
session.totals, precision=precision, rounding=rounding
)
else:
error = processing_result.error
upload_obj.state = "error"
Expand All @@ -1092,6 +1100,12 @@ def update_upload_with_processing_result(
db_session.flush()

def save_report(self, commit: Commit, report: Report, report_code=None):
rounding: str = read_yaml_field(
self.current_yaml, ("coverage", "round"), "nearest"
)
precision: int = read_yaml_field(
self.current_yaml, ("coverage", "precision"), 2
)
if len(report._chunks) > 2 * len(report._files) and len(report._files) > 0:
report.repack()
archive_service = self.get_archive_service(commit.repository)
Expand Down Expand Up @@ -1144,7 +1158,10 @@ def save_report(self, commit: Commit, report: Report, report_code=None):
if report_totals is None:
report_totals = ReportLevelTotals(report_id=commit.report.id)
db_session.add(report_totals)
report_totals.update_from_totals(report.totals)

report_totals.update_from_totals(
report.totals, precision=precision, rounding=rounding
)
db_session.flush()
log.info(
"Archived report",
Expand Down Expand Up @@ -1172,6 +1189,12 @@ def save_full_report(self, commit: Commit, report: Report, report_code=None):
Returns:
TYPE: Description
"""
rounding: str = read_yaml_field(
self.current_yaml, ("coverage", "round"), "nearest"
)
precision: int = read_yaml_field(
self.current_yaml, ("coverage", "precision"), 2
)
res = self.save_report(commit, report, report_code)
db_session = commit.get_db_session()
for sess_id, session in report.sessions.items():
Expand Down Expand Up @@ -1200,7 +1223,9 @@ def save_full_report(self, commit: Commit, report: Report, report_code=None):
if session.totals is not None:
upload_totals = UploadLevelTotals(upload_id=upload.id_)
db_session.add(upload_totals)
upload_totals.update_from_totals(session.totals)
upload_totals.update_from_totals(
session.totals, precision=precision, rounding=rounding
)
return res

async def save_parallel_report_to_archive(
Expand Down
13 changes: 5 additions & 8 deletions services/yaml/reader.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import logging
from decimal import ROUND_CEILING, ROUND_FLOOR, ROUND_HALF_EVEN, Decimal
from decimal import Decimal
from typing import Any, List, Mapping

from shared.yaml.user_yaml import UserYaml

from helpers.components import Component
from helpers.number import precise_round

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -32,14 +33,10 @@ def get_minimum_precision(yaml_dict: Mapping[str, Any]) -> Decimal:
return Decimal("0.1") ** precision


def round_number(yaml_dict: UserYaml, number: Decimal):
def round_number(yaml_dict: UserYaml, number: Decimal) -> Decimal:
rounding = read_yaml_field(yaml_dict, ("coverage", "round"), "nearest")
quantizer = get_minimum_precision(yaml_dict)
if rounding == "up":
return number.quantize(quantizer, rounding=ROUND_CEILING)
if rounding == "down":
return number.quantize(quantizer, rounding=ROUND_FLOOR)
return number.quantize(quantizer, rounding=ROUND_HALF_EVEN)
precision = read_yaml_field(yaml_dict, ("coverage", "precision"), 2)
return precise_round(number, precision=precision, rounding=rounding)


def get_paths_from_flags(yaml_dict: UserYaml, flags):
Expand Down

0 comments on commit 07653e1

Please sign in to comment.