metripy 0.2.7__py3-none-any.whl → 0.3.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of metripy might be problematic. Click here for more details.

Files changed (67) hide show
  1. metripy/Application/Analyzer.py +23 -3
  2. metripy/Application/Application.py +16 -2
  3. metripy/Application/Config/Config.py +34 -0
  4. metripy/Application/Config/File/ConfigFileReaderFactory.py +6 -5
  5. metripy/Application/Config/File/ConfigFileReaderInterface.py +70 -3
  6. metripy/Application/Config/File/JsonConfigFileReader.py +5 -70
  7. metripy/Application/Config/File/YamlConfigFileReader.py +17 -0
  8. metripy/Application/Config/Parser.py +24 -11
  9. metripy/Application/Config/ProjectConfig.py +64 -0
  10. metripy/Application/Info.py +61 -0
  11. metripy/Dependency/Dependency.py +17 -1
  12. metripy/Dependency/Pip/Pip.py +21 -31
  13. metripy/Dependency/Pip/PyPi.py +1 -0
  14. metripy/Git/GitAnalyzer.py +0 -3
  15. metripy/Import/Json/JsonImporter.py +17 -0
  16. metripy/LangAnalyzer/AbstractLangAnalyzer.py +4 -3
  17. metripy/LangAnalyzer/Php/PhpAnalyzer.py +2 -1
  18. metripy/LangAnalyzer/Python/PythonAnalyzer.py +31 -9
  19. metripy/LangAnalyzer/Python/PythonHalSteadAnalyzer.py +55 -0
  20. metripy/LangAnalyzer/Typescript/TypescriptAnalyzer.py +12 -9
  21. metripy/LangAnalyzer/Typescript/TypescriptAstParser.py +1 -1
  22. metripy/Metric/Code/AggregatedMetrics.py +12 -5
  23. metripy/Metric/Code/FileMetrics.py +32 -1
  24. metripy/Metric/Code/ModuleMetrics.py +5 -5
  25. metripy/Metric/Code/SegmentedMetrics.py +72 -36
  26. metripy/Metric/Code/Segmentor.py +44 -0
  27. metripy/Metric/FileTree/FileTreeParser.py +0 -4
  28. metripy/Metric/Git/GitMetrics.py +1 -1
  29. metripy/Metric/ProjectMetrics.py +29 -0
  30. metripy/Metric/Trend/AggregatedTrendMetric.py +101 -0
  31. metripy/Metric/Trend/ClassTrendMetric.py +20 -0
  32. metripy/Metric/Trend/FileTrendMetric.py +46 -0
  33. metripy/Metric/Trend/FunctionTrendMetric.py +28 -0
  34. metripy/Metric/Trend/SegmentedTrendMetric.py +29 -0
  35. metripy/Report/Html/DependencyPageRenderer.py +21 -0
  36. metripy/Report/Html/FilesPageRenderer.py +28 -0
  37. metripy/Report/Html/GitAnalysisPageRenderer.py +55 -0
  38. metripy/Report/Html/IndexPageRenderer.py +47 -0
  39. metripy/Report/Html/PageRenderer.py +43 -0
  40. metripy/Report/Html/PageRendererFactory.py +37 -0
  41. metripy/Report/Html/Reporter.py +78 -137
  42. metripy/Report/Html/TopOffendersPageRenderer.py +84 -0
  43. metripy/Report/Html/TrendsPageRenderer.py +137 -0
  44. metripy/Report/Json/GitJsonReporter.py +3 -0
  45. metripy/Report/Json/JsonReporter.py +6 -2
  46. metripy/Report/ReporterFactory.py +6 -3
  47. metripy/Tree/ClassNode.py +21 -0
  48. metripy/Tree/FunctionNode.py +66 -1
  49. metripy/Trend/TrendAnalyzer.py +150 -0
  50. metripy/templates/html_report/css/styles.css +1386 -0
  51. metripy/templates/html_report/dependencies.html +411 -0
  52. metripy/templates/html_report/files.html +1080 -0
  53. metripy/templates/html_report/git_analysis.html +325 -0
  54. metripy/templates/html_report/images/logo.svg +31 -0
  55. metripy/templates/html_report/index.html +374 -0
  56. metripy/templates/html_report/js/charts.js +313 -0
  57. metripy/templates/html_report/js/dashboard.js +546 -0
  58. metripy/templates/html_report/js/git_analysis.js +383 -0
  59. metripy/templates/html_report/top_offenders.html +267 -0
  60. metripy/templates/html_report/trends.html +468 -0
  61. {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/METADATA +27 -9
  62. metripy-0.3.6.dist-info/RECORD +96 -0
  63. {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/licenses/LICENSE +1 -1
  64. metripy-0.2.7.dist-info/RECORD +0 -66
  65. {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/WHEEL +0 -0
  66. {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/entry_points.txt +0 -0
  67. {metripy-0.2.7.dist-info → metripy-0.3.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,101 @@
1
+ from metripy.Metric.Code.SegmentedMetrics import SegmentedMetrics
2
+ from metripy.Metric.Trend.SegmentedTrendMetric import SegmentedTrendMetric
3
+
4
+
5
+ class AggregatedTrendMetric:
6
+ def __init__(
7
+ self,
8
+ historical_loc: int,
9
+ loc: int,
10
+ historical_avgCcPerFunction: float,
11
+ avgCcPerFunction: float,
12
+ historical_maintainabilityIndex: float,
13
+ maintainabilityIndex: float,
14
+ historical_avgLocPerFunction: float,
15
+ avgLocPerFunction: float,
16
+ historical_num_files: int,
17
+ num_files: int,
18
+ historical_segmented_loc: SegmentedMetrics,
19
+ segmented_loc: SegmentedMetrics,
20
+ historical_segmented_complexity: SegmentedMetrics,
21
+ segmented_complexity: SegmentedMetrics,
22
+ historical_segmented_maintainability: SegmentedMetrics,
23
+ segmented_maintainability: SegmentedMetrics,
24
+ historical_segmented_method_size: SegmentedMetrics,
25
+ segmented_method_size: SegmentedMetrics,
26
+ ):
27
+ self.historical_loc = historical_loc
28
+ self.loc_delta = loc - historical_loc
29
+ self.historical_avgCcPerFunction = historical_avgCcPerFunction
30
+ self.avgCcPerFunction_delta = avgCcPerFunction - historical_avgCcPerFunction
31
+ self.historical_maintainabilityIndex = historical_maintainabilityIndex
32
+ self.maintainabilityIndex_delta = (
33
+ maintainabilityIndex - historical_maintainabilityIndex
34
+ )
35
+ self.historical_avgLocPerFunction = historical_avgLocPerFunction
36
+ self.avgLocPerFunction_delta = avgLocPerFunction - historical_avgLocPerFunction
37
+ self.historical_num_files = historical_num_files
38
+ self.num_files_delta = num_files - historical_num_files
39
+
40
+ self.historical_segmentation_data = {
41
+ "loc": historical_segmented_loc,
42
+ "complexity": historical_segmented_complexity,
43
+ "maintainability": historical_segmented_maintainability,
44
+ "methodSize": historical_segmented_method_size,
45
+ }
46
+
47
+ self.segmentation_data_deltas = {
48
+ "loc": SegmentedTrendMetric(historical_segmented_loc, segmented_loc),
49
+ "complexity": SegmentedTrendMetric(
50
+ historical_segmented_complexity, segmented_complexity
51
+ ),
52
+ "maintainability": SegmentedTrendMetric(
53
+ historical_segmented_maintainability, segmented_maintainability
54
+ ),
55
+ "methodSize": SegmentedTrendMetric(
56
+ historical_segmented_method_size, segmented_method_size
57
+ ),
58
+ }
59
+
60
+ def get_trend_type(self, delta: float, up_is_good: bool) -> str:
61
+ if up_is_good:
62
+ return "positive" if delta > 0 else "negative" if delta < 0 else "neutral"
63
+ else:
64
+ return "negative" if delta > 0 else "positive" if delta < 0 else "neutral"
65
+
66
+ def get_trend_icon(self, delta: float) -> str:
67
+ return "arrow-up" if delta > 0 else "arrow-down" if delta < 0 else "arrow-right"
68
+
69
+ def to_dict(self) -> dict:
70
+ return {
71
+ "loc_delta": round(self.loc_delta, 2),
72
+ "loc_trend_type": self.get_trend_type(self.loc_delta, False),
73
+ "loc_trend_icon": self.get_trend_icon(self.loc_delta),
74
+ "avgCcPerFunction_delta": round(self.avgCcPerFunction_delta, 2),
75
+ "avgCcPerFunction_trend_type": self.get_trend_type(
76
+ self.avgCcPerFunction_delta, False
77
+ ),
78
+ "avgCcPerFunction_trend_icon": self.get_trend_icon(
79
+ self.avgCcPerFunction_delta
80
+ ),
81
+ "maintainabilityIndex_delta": round(self.maintainabilityIndex_delta, 2),
82
+ "maintainabilityIndex_trend_type": self.get_trend_type(
83
+ self.maintainabilityIndex_delta, True
84
+ ),
85
+ "maintainabilityIndex_trend_icon": self.get_trend_icon(
86
+ self.maintainabilityIndex_delta
87
+ ),
88
+ "avgLocPerFunction_delta": round(self.avgLocPerFunction_delta, 2),
89
+ "avgLocPerFunction_trend_type": self.get_trend_type(
90
+ self.avgLocPerFunction_delta, False
91
+ ),
92
+ "avgLocPerFunction_trend_icon": self.get_trend_icon(
93
+ self.avgLocPerFunction_delta
94
+ ),
95
+ "num_files_delta": self.num_files_delta,
96
+ "num_files_trend_type": self.get_trend_type(self.num_files_delta, False),
97
+ "num_files_trend_icon": self.get_trend_icon(self.num_files_delta),
98
+ }
99
+
100
+ def to_dict_segmentation(self) -> dict:
101
+ return {k: v.to_dict() for k, v in self.segmentation_data_deltas.items()}
@@ -0,0 +1,20 @@
1
+ class ClassTrendMetric:
2
+ def __init__(
3
+ self,
4
+ historical_lineno: int,
5
+ lineno: int,
6
+ historical_real_complexity: int,
7
+ real_complexity: int,
8
+ ):
9
+ self.historical_lineno = historical_lineno
10
+ self.lineno_delta = lineno - historical_lineno
11
+ self.historical_real_complexity = historical_real_complexity
12
+ self.real_complexity_delta = real_complexity - historical_real_complexity
13
+
14
+ def to_dict(self) -> dict:
15
+ return {
16
+ "historical_lineno": self.historical_lineno,
17
+ "lineno_delta": self.lineno_delta,
18
+ "historical_real_complexity": self.historical_real_complexity,
19
+ "real_complexity_delta": self.real_complexity_delta,
20
+ }
@@ -0,0 +1,46 @@
1
+ class FileTrendMetric:
2
+ def __init__(
3
+ self,
4
+ historical_loc: int,
5
+ loc: int,
6
+ historical_totalCc: int,
7
+ totalCc: int,
8
+ historical_avgCcPerFunction: float,
9
+ avgCcPerFunction: float,
10
+ historical_maintainabilityIndex: float,
11
+ maintainabilityIndex: float,
12
+ historical_avgLocPerFunction: float,
13
+ avgLocPerFunction: float,
14
+ ):
15
+ self.historical_loc = historical_loc
16
+ self.loc_delta = loc - historical_loc
17
+
18
+ self.historical_totalCc = historical_totalCc
19
+ self.totalCc_delta = totalCc - historical_totalCc
20
+
21
+ self.historical_avgCcPerFunction = historical_avgCcPerFunction
22
+ self.avgCcPerFunction_delta = avgCcPerFunction - historical_avgCcPerFunction
23
+
24
+ self.maintainabilityIndex_delta = (
25
+ maintainabilityIndex - historical_maintainabilityIndex
26
+ )
27
+ self.historical_maintainabilityIndex = historical_maintainabilityIndex
28
+
29
+ self.avgLocPerFunction_delta = avgLocPerFunction - historical_avgLocPerFunction
30
+ self.historical_avgLocPerFunction = historical_avgLocPerFunction
31
+
32
+ def to_dict(self) -> dict:
33
+ return {
34
+ "historical_loc": round(self.historical_loc, 2),
35
+ "loc_delta": round(self.loc_delta, 2),
36
+ "historical_totalCc": round(self.historical_totalCc, 2),
37
+ "totalCc_delta": round(self.totalCc_delta, 2),
38
+ "historical_avgCcPerFunction": round(self.historical_avgCcPerFunction, 2),
39
+ "avgCcPerFunction_delta": round(self.avgCcPerFunction_delta, 2),
40
+ "historical_maintainabilityIndex": round(
41
+ self.historical_maintainabilityIndex, 2
42
+ ),
43
+ "maintainabilityIndex_delta": round(self.maintainabilityIndex_delta, 2),
44
+ "historical_avgLocPerFunction": round(self.historical_avgLocPerFunction, 2),
45
+ "avgLocPerFunction_delta": round(self.avgLocPerFunction_delta, 2),
46
+ }
@@ -0,0 +1,28 @@
1
+ class FunctionTrendMetric:
2
+ def __init__(
3
+ self,
4
+ historical_loc: int,
5
+ loc: int,
6
+ historical_complexity: int,
7
+ complexity: int,
8
+ historical_maintainability_index: float,
9
+ maintainability_index: float,
10
+ ):
11
+ self.historical_loc = historical_loc
12
+ self.loc_delta = loc - historical_loc
13
+ self.historical_complexity = historical_complexity
14
+ self.complexity_delta = complexity - historical_complexity
15
+ self.historical_maintainability_index = historical_maintainability_index
16
+ self.maintainability_index_delta = (
17
+ maintainability_index - historical_maintainability_index
18
+ )
19
+
20
+ def to_dict(self) -> dict:
21
+ return {
22
+ "historical_loc": self.historical_loc,
23
+ "loc_delta": self.loc_delta,
24
+ "historical_complexity": self.historical_complexity,
25
+ "complexity_delta": self.complexity_delta,
26
+ "historical_maintainability_index": self.historical_maintainability_index,
27
+ "maintainability_index_delta": self.maintainability_index_delta,
28
+ }
@@ -0,0 +1,29 @@
1
+ from metripy.Metric.Code.SegmentedMetrics import SegmentedMetrics
2
+
3
+
4
+ class SegmentedTrendMetric:
5
+ def __init__(
6
+ self,
7
+ historical_segmentation: SegmentedMetrics,
8
+ segmentation: SegmentedMetrics,
9
+ ):
10
+ self.historical_good = historical_segmentation.good
11
+ self.good_delta = segmentation.good - historical_segmentation.good
12
+ self.historical_ok = historical_segmentation.ok
13
+ self.ok_delta = segmentation.ok - historical_segmentation.ok
14
+ self.historical_warning = historical_segmentation.warning
15
+ self.warning_delta = segmentation.warning - historical_segmentation.warning
16
+ self.historical_critical = historical_segmentation.critical
17
+ self.critical_delta = segmentation.critical - historical_segmentation.critical
18
+
19
+ def to_dict(self) -> dict:
20
+ return {
21
+ "historical_good": self.historical_good,
22
+ "good_delta": self.good_delta,
23
+ "historical_ok": self.historical_ok,
24
+ "ok_delta": self.ok_delta,
25
+ "historical_warning": self.historical_warning,
26
+ "warning_delta": self.warning_delta,
27
+ "historical_critical": self.historical_critical,
28
+ "critical_delta": self.critical_delta,
29
+ }
@@ -0,0 +1,21 @@
1
+ from metripy.Dependency.Dependency import Dependency
2
+ from metripy.Metric.ProjectMetrics import ProjectMetrics
3
+ from metripy.Report.Html.PageRenderer import PageRenderer
4
+ import json
5
+
6
+ class DependencyPageRenderer(PageRenderer):
7
+ def __init__(self, template_dir: str, output_dir: str, project_name: str):
8
+ super().__init__(template_dir, output_dir, project_name)
9
+
10
+ def render(self, metrics: ProjectMetrics):
11
+ dependencies = metrics.dependencies if metrics.dependencies is not None else []
12
+ license_by_type = Dependency.get_lisence_distribution(dependencies)
13
+
14
+ self.render_template(
15
+ "dependencies.html",
16
+ {
17
+ "has_dependencies_data": bool(metrics.dependencies),
18
+ "dependencies": [d.to_dict() for d in dependencies],
19
+ "license_distribution_json": json.dumps(license_by_type, indent=2),
20
+ },
21
+ )
@@ -0,0 +1,28 @@
1
+ import json
2
+
3
+ from metripy.Metric.FileTree.FileTreeParser import FileTreeParser
4
+ from metripy.Metric.ProjectMetrics import ProjectMetrics
5
+ from metripy.Report.Html.PageRenderer import PageRenderer
6
+
7
+
8
+ class FilesPageRenderer(PageRenderer):
9
+ def __init__(self, template_dir: str, output_dir: str, project_name: str):
10
+ super().__init__(template_dir, output_dir, project_name)
11
+
12
+ def render(self, metrics: ProjectMetrics):
13
+ file_names = []
14
+ file_details = {}
15
+ for file_metrics in metrics.file_metrics:
16
+ file_name = file_metrics.full_name
17
+ file_details[file_name] = file_metrics.to_dict()
18
+ file_names.append(file_name)
19
+
20
+ filetree = FileTreeParser.parse(file_names, shorten=True)
21
+
22
+ self.render_template(
23
+ "files.html",
24
+ {
25
+ "filetree": json.dumps(filetree.to_dict()),
26
+ "file_details": json.dumps(file_details),
27
+ },
28
+ )
@@ -0,0 +1,55 @@
1
+ import json
2
+
3
+ from metripy.Metric.ProjectMetrics import ProjectMetrics
4
+ from metripy.Report.Html.PageRenderer import PageRenderer
5
+
6
+
7
+ class GitAnalysisPageRenderer(PageRenderer):
8
+ def __init__(self, template_dir: str, output_dir: str, project_name: str):
9
+ super().__init__(template_dir, output_dir, project_name)
10
+
11
+ def _render_empty_template(self):
12
+ self.render_template(
13
+ "git_analysis.html",
14
+ {
15
+ "has_git_analysis_data": False,
16
+ "git_analysis": {},
17
+ "git_analysis_json": "{}",
18
+ "git_stats_data": "{}",
19
+ "git_churn_data": "{}",
20
+ "git_silos_data": [],
21
+ "git_contributors": [],
22
+ "git_hotspots_data": [],
23
+ },
24
+ )
25
+
26
+ def render(self, metrics: ProjectMetrics):
27
+ if not metrics.git_metrics:
28
+ self._render_empty_template()
29
+ return
30
+
31
+ self.render_template(
32
+ "git_analysis.html",
33
+ {
34
+ "has_git_analysis_data": bool(metrics.git_metrics),
35
+ "git_analysis": metrics.git_metrics.to_dict(),
36
+ "git_analysis_json": json.dumps(
37
+ metrics.git_metrics.get_contributors_dict(), indent=4
38
+ ),
39
+ "git_stats_data": json.dumps(
40
+ metrics.git_metrics.get_commit_stats_per_month(), indent=4
41
+ ), # git commit graph
42
+ "git_churn_data": json.dumps(
43
+ metrics.git_metrics.get_churn_per_month(), indent=4
44
+ ), # git chrun graph
45
+ "git_silos_data": metrics.git_metrics.get_silos_list()[
46
+ :10
47
+ ], # silos list
48
+ "git_contributors": metrics.git_metrics.get_contributors_list()[
49
+ :10
50
+ ], # contributors list
51
+ "git_hotspots_data": metrics.git_metrics.get_hotspots_list()[
52
+ :10
53
+ ], # hotspots list
54
+ },
55
+ )
@@ -0,0 +1,47 @@
1
+ import json
2
+
3
+ from metripy.Metric.ProjectMetrics import ProjectMetrics
4
+ from metripy.Report.Html.PageRenderer import PageRenderer
5
+
6
+ from metripy.Dependency.Dependency import Dependency
7
+
8
+
9
+ class IndexPageRenderer(PageRenderer):
10
+ def __init__(self, template_dir: str, output_dir: str, project_name: str):
11
+ super().__init__(template_dir, output_dir, project_name)
12
+
13
+ def render(self, metrics: ProjectMetrics):
14
+ git_stats_data = {}
15
+ if metrics.git_metrics:
16
+ git_stats_data = metrics.git_metrics.get_commit_stats_per_month()
17
+
18
+
19
+ dependencies = metrics.dependencies if metrics.dependencies is not None else []
20
+ license_by_type = Dependency.get_lisence_distribution(dependencies)
21
+
22
+ self.render_template(
23
+ "index.html",
24
+ {
25
+ "git_stats_data": json.dumps(git_stats_data, indent=4),
26
+ "license_distribution_json": json.dumps(license_by_type, indent=2),
27
+ "total_code_metrics": metrics.total_code_metrics.to_dict(),
28
+ "has_total_code_metrics_trend": metrics.total_code_metrics.trend
29
+ is not None,
30
+ "total_code_metrics_trend": (
31
+ metrics.total_code_metrics.trend.to_dict()
32
+ if metrics.total_code_metrics.trend
33
+ else None
34
+ ),
35
+ "segmentation_data": json.dumps(
36
+ metrics.total_code_metrics.to_dict_segmentation(), indent=4
37
+ ),
38
+ "segmentation_data_trend": (
39
+ json.dumps(
40
+ metrics.total_code_metrics.trend.to_dict_segmentation(),
41
+ indent=4,
42
+ )
43
+ if metrics.total_code_metrics.trend
44
+ else None
45
+ ),
46
+ },
47
+ )
@@ -0,0 +1,43 @@
1
+ import os
2
+ from datetime import datetime
3
+
4
+ from py_template_engine import TemplateEngine
5
+
6
+ from metripy.Application.Info import Info
7
+
8
+
9
+ class PageRenderer:
10
+ def __init__(self, template_dir: str, output_dir: str, project_name: str):
11
+ self.template_dir = template_dir
12
+ self.output_dir = output_dir
13
+
14
+ self.global_template_args = {
15
+ "project_name": project_name,
16
+ "last_updated": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
17
+ "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
18
+ "author": "Metripy",
19
+ "version": Info().get_version(),
20
+ }
21
+
22
+ @staticmethod
23
+ def _stringify_values(obj):
24
+ if isinstance(obj, dict):
25
+ return {
26
+ key: PageRenderer._stringify_values(value) for key, value in obj.items()
27
+ }
28
+ elif isinstance(obj, list):
29
+ return [PageRenderer._stringify_values(item) for item in obj]
30
+ else:
31
+ return str(obj)
32
+
33
+ def render_template(self, template_name: str, data: dict):
34
+ data = self._stringify_values(
35
+ {
36
+ **self.global_template_args,
37
+ **data,
38
+ }
39
+ )
40
+ engine = TemplateEngine(os.path.join(self.template_dir, template_name))
41
+ content = engine.render(**data)
42
+ with open(os.path.join(self.output_dir, template_name), "w") as file:
43
+ file.write(content)
@@ -0,0 +1,37 @@
1
+ from metripy.Report.Html.DependencyPageRenderer import DependencyPageRenderer
2
+ from metripy.Report.Html.FilesPageRenderer import FilesPageRenderer
3
+ from metripy.Report.Html.GitAnalysisPageRenderer import GitAnalysisPageRenderer
4
+ from metripy.Report.Html.IndexPageRenderer import IndexPageRenderer
5
+ from metripy.Report.Html.TopOffendersPageRenderer import TopOffendersPageRenderer
6
+ from metripy.Report.Html.TrendsPageRenderer import TrendsPageRenderer
7
+
8
+
9
+ class PageRendererFactory:
10
+ def __init__(self, template_dir: str, output_dir: str, project_name: str):
11
+ self.template_dir = template_dir
12
+ self.output_dir = output_dir
13
+ self.project_name = project_name
14
+
15
+ def create_index_page_renderer(self) -> IndexPageRenderer:
16
+ return IndexPageRenderer(self.template_dir, self.output_dir, self.project_name)
17
+
18
+ def create_files_page_renderer(self) -> FilesPageRenderer:
19
+ return FilesPageRenderer(self.template_dir, self.output_dir, self.project_name)
20
+
21
+ def create_top_offenders_page_renderer(self) -> TopOffendersPageRenderer:
22
+ return TopOffendersPageRenderer(
23
+ self.template_dir, self.output_dir, self.project_name
24
+ )
25
+
26
+ def create_git_analysis_page_renderer(self) -> GitAnalysisPageRenderer:
27
+ return GitAnalysisPageRenderer(
28
+ self.template_dir, self.output_dir, self.project_name
29
+ )
30
+
31
+ def create_dependency_page_renderer(self) -> DependencyPageRenderer:
32
+ return DependencyPageRenderer(
33
+ self.template_dir, self.output_dir, self.project_name
34
+ )
35
+
36
+ def create_trends_page_renderer(self) -> TrendsPageRenderer:
37
+ return TrendsPageRenderer(self.template_dir, self.output_dir, self.project_name)