Skip to content

Commit 1df2d79

Browse files
authored
Report generators (#65)
* feat: add CriticalValueReportBuilder implementation. * feat: add TimeComplexityReportBuilder implementation. * feat: add template with chart for time-complexity reports * feat: fully implemented PowerReportBuilder, TimeComplexityReportBuilder and CriticalValueReportBuilder. * feat: add "report-mode" command to experiment configuration. * test: add unit-tests. * fix: fixed errors * fix: fixed errors * fix: auto-format code with ruff formatter * fix: resolve mypy type checking errors * fix: remove unused experiment_storage parameter. * refactor: add report examples.
1 parent daa6638 commit 1df2d79

File tree

31 files changed

+1395
-75
lines changed

31 files changed

+1395
-75
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ dependencies = [
1414
"matplotlib>=3.8.0",
1515
"tqdm>=4.66.2",
1616
"fpdf2>=2.7.8",
17+
"jinja2>=3.1.5",
18+
"xhtml2pdf>=0.2.17",
1719
"pandas>=2.2.1",
1820
"sqlalchemy>=2.0.36",
1921
"python-rapidjson==1.20",
408 KB
Binary file not shown.
150 KB
Binary file not shown.

stattest/cli/cli/cli.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from stattest.cli.commands.configure.report_builder_type.report_builder_type import (
1515
report_builder_type,
1616
)
17+
from stattest.cli.commands.configure.report_mode.report_mode import report_mode
1718
from stattest.cli.commands.configure.run_mode.run_mode import run_mode
1819
from stattest.cli.commands.configure.sample_sizes.sample_sizes import sample_sizes
1920
from stattest.cli.commands.configure.show.show import show
@@ -58,4 +59,5 @@ def _create_experiments_dir() -> None:
5859
cli.add_command(significance_levels)
5960
cli.add_command(criteria)
6061
cli.add_command(alternatives)
62+
cli.add_command(report_mode)
6163
cli.add_command(build_and_run)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from click import Context, argument, echo, pass_context
2+
3+
from stattest.cli.commands.common.common import (
4+
get_experiment_name_and_config,
5+
save_experiment_config,
6+
)
7+
from stattest.cli.commands.configure.configure import configure
8+
from stattest.validation.cli.commands.configure.report_mode.report_mode import validate_report_mode
9+
10+
11+
@configure.command()
12+
@argument("mode")
13+
@pass_context
14+
def report_mode(ctx: Context, mode: str) -> None:
15+
"""
16+
Configure experiment report mode.
17+
18+
:param ctx: context.
19+
:param mode: report mode.
20+
"""
21+
22+
validate_report_mode(mode)
23+
mode_lower = mode.lower()
24+
25+
experiment_name, experiment_config = get_experiment_name_and_config(ctx)
26+
27+
experiment_config["report_mode"] = mode_lower
28+
29+
save_experiment_config(ctx, experiment_name, experiment_config)
30+
31+
echo(f"Report mode of the experiment '{experiment_name}' is set to '{mode_lower}'.")

stattest/cli/commands/create/create.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def create(name: str) -> None:
2424
"executor_type": "standard",
2525
"report_builder_type": "standard",
2626
"run_mode": "reuse",
27+
"report_mode": "with-chart",
2728
},
2829
}
2930

stattest/configuration/experiment_config/experiment_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from stattest.configuration.model.criterion.criterion import Criterion
44
from stattest.configuration.model.experiment_type.experiment_type import ExperimentType
55
from stattest.configuration.model.hypothesis.hypothesis import Hypothesis
6+
from stattest.configuration.model.report_mode.report_mode import ReportMode
67
from stattest.configuration.model.run_mode.run_mode import RunMode
78
from stattest.configuration.model.step_type.step_type import StepType
89

@@ -23,3 +24,4 @@ class ExperimentConfig:
2324
sample_sizes: list[int]
2425
monte_carlo_count: int
2526
criteria: list[Criterion]
27+
report_mode: ReportMode
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from enum import Enum
2+
3+
4+
class ReportMode(Enum):
5+
"""
6+
Report mode (build report with chart or without chart).
7+
"""
8+
9+
WITH_CHART = "with-chart"
10+
WITHOUT_CHART = "without-chart"

stattest/experiment_new/step/report_building/critical_value/critical_value.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from pathlib import Path
22

3+
from pysatl_criterion.cv_calculator.cv_calculator.cv_calculator import CVCalculator
34
from pysatl_criterion.persistence.model.limit_distribution.limit_distribution import (
45
ILimitDistributionStorage,
56
LimitDistributionQuery,
67
)
78
from stattest.configuration.criteria_config.criteria_config import CriterionConfig
9+
from stattest.configuration.model.report_mode.report_mode import ReportMode
810
from stattest.report.critical_value.critical_value import CriticalValueReportBuilder
911

1012

@@ -21,36 +23,41 @@ def __init__(
2123
monte_carlo_count: int,
2224
result_storage: ILimitDistributionStorage,
2325
results_path: Path,
26+
with_chart: ReportMode,
2427
):
2528
self.criteria_config = criteria_config
2629
self.significance_levels = significance_levels
27-
self.sizes = sample_sizes
30+
self.sizes = sorted(sample_sizes)
2831
self.monte_carlo_count = monte_carlo_count
2932
self.result_storage = result_storage
3033
self.results_path = results_path
34+
self.with_chart = with_chart
3135

3236
def run(self) -> None:
3337
"""
3438
Run standard critical value report building step.
3539
"""
3640

41+
cv_values = []
3742
for criterion_config in self.criteria_config:
3843
for sample_size in self.sizes:
39-
limit_distribution = self._get_limit_distribution_from_storage(
40-
storage=self.result_storage,
41-
criterion_config=criterion_config,
42-
sample_size=sample_size,
43-
monte_carlo_count=self.monte_carlo_count,
44-
)
44+
cv_calculator = CVCalculator(self.result_storage)
4545
for significance_level in self.significance_levels:
46-
report_builder = CriticalValueReportBuilder(
47-
criterion_config=criterion_config,
48-
sample_size=sample_size,
49-
significance_level=significance_level,
50-
limit_distribution=limit_distribution,
51-
results_path=self.results_path,
46+
cv_value = cv_calculator.calculate_critical_value(
47+
criterion_config.criterion_code, sample_size, significance_level
5248
)
53-
report_builder.build()
49+
50+
cv_values.append(cv_value)
51+
52+
report_builder = CriticalValueReportBuilder(
53+
criteria_config=self.criteria_config,
54+
sample_sizes=self.sizes,
55+
significance_levels=self.significance_levels,
56+
cv_values=cv_values,
57+
results_path=self.results_path,
58+
with_chart=self.with_chart,
59+
)
60+
report_builder.build()
5461

5562
def _get_limit_distribution_from_storage(
5663
self,
@@ -59,16 +66,14 @@ def _get_limit_distribution_from_storage(
5966
sample_size: int,
6067
monte_carlo_count: int,
6168
) -> list[float]:
62-
"""
63-
Get limit distribution from storage.
69+
"""Get limit distribution from storage.
6470
6571
:param storage: storage.
6672
:param criterion_config: criterion configuration.
6773
:param sample_size: sample size.
6874
:param monte_carlo_count: monte carlo count.
6975
70-
:return: limit distribution.
71-
"""
76+
:return: limit distribution."""
7277

7378
query = LimitDistributionQuery(
7479
criterion_code=criterion_config.criterion_code,

stattest/experiment_new/step/report_building/power/power.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from stattest.configuration.criteria_config.criteria_config import CriterionConfig
44
from stattest.configuration.model.alternative.alternative import Alternative
5+
from stattest.configuration.model.report_mode.report_mode import ReportMode
56
from stattest.persistence.model.power.power import IPowerStorage, PowerQuery
67
from stattest.report.power.power import PowerReportBuilder
78

@@ -20,40 +21,57 @@ def __init__(
2021
monte_carlo_count: int,
2122
result_storage: IPowerStorage,
2223
results_path: Path,
24+
with_chart: ReportMode,
2325
):
2426
self.criteria_config = criteria_config
2527
self.significance_levels = significance_levels
2628
self.alternatives = alternatives
27-
self.sizes = sample_sizes
29+
self.sizes = sorted(sample_sizes)
2830
self.monte_carlo_count = monte_carlo_count
2931
self.result_storage = result_storage
3032
self.results_path = results_path
33+
self.with_chart = with_chart
3134

3235
def run(self) -> None:
3336
"""
3437
Run standard power report building step.
3538
"""
3639

40+
power_data = self._collect_statistics()
41+
42+
builder = PowerReportBuilder(
43+
criteria_config=self.criteria_config,
44+
sample_sizes=self.sizes,
45+
alternatives=self.alternatives,
46+
significance_levels=self.significance_levels,
47+
power_result=power_data,
48+
results_path=self.results_path,
49+
with_chart=self.with_chart,
50+
)
51+
builder.build()
52+
53+
def _collect_statistics(self) -> dict[str, dict[tuple[str, float], dict[int, list[bool]]]]:
54+
"""
55+
Collect power results.
56+
57+
:return: {criterion_code -> (alt_name, alpha) -> {size: [bool]}}
58+
"""
59+
60+
power_data: dict[str, dict[tuple[str, float], dict[int, list[bool]]]] = {}
61+
3762
for criterion_config in self.criteria_config:
3863
for alternative in self.alternatives:
39-
for sample_size in self.sizes:
40-
for significance_level in self.significance_levels:
41-
results_criteria = self._get_power_result_from_storage(
42-
criterion_config=criterion_config,
43-
sample_size=sample_size,
44-
alternative=alternative,
45-
significance_level=significance_level,
46-
)
47-
48-
report_builder = PowerReportBuilder(
64+
for significance_level in self.significance_levels:
65+
for sample_size in self.sizes:
66+
result = self._get_power_result_from_storage(
4967
criterion_config=criterion_config,
5068
sample_size=sample_size,
5169
alternative=alternative,
5270
significance_level=significance_level,
53-
power_result=results_criteria,
54-
results_path=self.results_path,
5571
)
56-
report_builder.build()
72+
key = (alternative.generator_name, significance_level)
73+
power_data[criterion_config.criterion_code][key][sample_size] = result
74+
return power_data
5775

5876
def _get_power_result_from_storage(
5977
self,

0 commit comments

Comments
 (0)