metripy 0.2.5__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 (66) hide show
  1. metripy-0.2.5.dist-info/METADATA +112 -0
  2. metripy-0.2.5.dist-info/RECORD +66 -0
  3. metripy-0.2.5.dist-info/WHEEL +5 -0
  4. metripy-0.2.5.dist-info/entry_points.txt +2 -0
  5. metripy-0.2.5.dist-info/licenses/LICENSE +21 -0
  6. metripy-0.2.5.dist-info/top_level.txt +1 -0
  7. src/Application/Analyzer.py +105 -0
  8. src/Application/Application.py +54 -0
  9. src/Application/Config/Config.py +13 -0
  10. src/Application/Config/File/ConfigFileReaderFactory.py +22 -0
  11. src/Application/Config/File/ConfigFileReaderInterface.py +14 -0
  12. src/Application/Config/File/JsonConfigFileReader.py +81 -0
  13. src/Application/Config/GitConfig.py +10 -0
  14. src/Application/Config/Parser.py +30 -0
  15. src/Application/Config/ProjectConfig.py +27 -0
  16. src/Application/Config/ReportConfig.py +10 -0
  17. src/Application/__init__.py +0 -0
  18. src/Component/Debug/Debugger.py +20 -0
  19. src/Component/File/Finder.py +37 -0
  20. src/Component/Output/CliOutput.py +49 -0
  21. src/Component/Output/ProgressBar.py +27 -0
  22. src/Dependency/Composer/Composer.py +30 -0
  23. src/Dependency/Composer/Packegist.py +55 -0
  24. src/Dependency/Dependency.py +30 -0
  25. src/Dependency/Npm/Npm.py +30 -0
  26. src/Dependency/Npm/NpmOrg.py +47 -0
  27. src/Dependency/Pip/Pip.py +69 -0
  28. src/Dependency/Pip/PyPi.py +49 -0
  29. src/Git/GitAnalyzer.py +86 -0
  30. src/LangAnalyzer/AbstractLangAnalyzer.py +65 -0
  31. src/LangAnalyzer/Generic/HalSteadAnalyzer.py +58 -0
  32. src/LangAnalyzer/Generic/__init__.py +0 -0
  33. src/LangAnalyzer/Php/PhpAnalyzer.py +193 -0
  34. src/LangAnalyzer/Php/PhpBasicAstParser.py +56 -0
  35. src/LangAnalyzer/Php/PhpBasicLocAnalyzer.py +174 -0
  36. src/LangAnalyzer/Php/PhpHalSteadAnalyzer.py +44 -0
  37. src/LangAnalyzer/Python/PythonAnalyzer.py +129 -0
  38. src/LangAnalyzer/Typescript/TypescriptAnalyzer.py +210 -0
  39. src/LangAnalyzer/Typescript/TypescriptAstParser.py +68 -0
  40. src/LangAnalyzer/Typescript/TypescriptBasicComplexityAnalyzer.py +114 -0
  41. src/LangAnalyzer/Typescript/TypescriptBasicLocAnalyzer.py +69 -0
  42. src/LangAnalyzer/Typescript/TypescriptHalSteadAnalyzer.py +55 -0
  43. src/LangAnalyzer/__init__.py +0 -0
  44. src/Metric/Code/AggregatedMetrics.py +42 -0
  45. src/Metric/Code/FileMetrics.py +33 -0
  46. src/Metric/Code/ModuleMetrics.py +32 -0
  47. src/Metric/Code/SegmentedMetrics.py +65 -0
  48. src/Metric/FileTree/FileTree.py +15 -0
  49. src/Metric/FileTree/FileTreeParser.py +42 -0
  50. src/Metric/Git/GitCodeHotspot.py +37 -0
  51. src/Metric/Git/GitContributor.py +37 -0
  52. src/Metric/Git/GitKnowledgeSilo.py +27 -0
  53. src/Metric/Git/GitMetrics.py +148 -0
  54. src/Metric/ProjectMetrics.py +55 -0
  55. src/Report/Csv/Reporter.py +12 -0
  56. src/Report/Html/Reporter.py +210 -0
  57. src/Report/Json/AbstractJsonReporter.py +10 -0
  58. src/Report/Json/GitJsonReporter.py +21 -0
  59. src/Report/Json/JsonReporter.py +12 -0
  60. src/Report/ReporterFactory.py +22 -0
  61. src/Report/ReporterInterface.py +17 -0
  62. src/Tree/ClassNode.py +32 -0
  63. src/Tree/FunctionNode.py +49 -0
  64. src/Tree/ModuleNode.py +42 -0
  65. src/__init__.py +0 -0
  66. src/codemetrics.py +15 -0
@@ -0,0 +1,210 @@
1
+ import math
2
+
3
+ import lizard
4
+
5
+ from Component.Output.ProgressBar import ProgressBar
6
+ from LangAnalyzer.AbstractLangAnalyzer import AbstractLangAnalyzer
7
+ from LangAnalyzer.Typescript.TypescriptAstParser import TypescriptAstParser
8
+ from LangAnalyzer.Typescript.TypescriptBasicComplexityAnalyzer import (
9
+ TypescriptBasicComplexityAnalzyer,
10
+ )
11
+ from LangAnalyzer.Typescript.TypescriptBasicLocAnalyzer import (
12
+ TypescriptBasicLocAnalyzer,
13
+ )
14
+ from LangAnalyzer.Typescript.TypescriptHalSteadAnalyzer import (
15
+ TypeScriptHalSteadAnalyzer,
16
+ )
17
+ from Tree.ClassNode import ClassNode
18
+ from Tree.FunctionNode import FunctionNode
19
+ from Tree.ModuleNode import ModuleNode
20
+
21
+
22
+ class TypescriptAnalyzer(AbstractLangAnalyzer):
23
+
24
+ def __init__(self):
25
+ super().__init__()
26
+ self.ast_parser = TypescriptAstParser()
27
+ self.halstead_analyzer = TypeScriptHalSteadAnalyzer()
28
+ self.basic_complexity_analyzer = TypescriptBasicComplexityAnalzyer()
29
+
30
+ def get_lang_name(self) -> str:
31
+ return "Typescript"
32
+
33
+ def set_files(self, files: list[str]) -> None:
34
+ self.files = list(filter(lambda file: file.endswith((".ts", "js")), files))
35
+
36
+ def is_needed(self) -> bool:
37
+ return len(self.files) > 0
38
+
39
+ def run(self, progress_bar: ProgressBar) -> None:
40
+ for file in self.files:
41
+ with open(file, "r") as f:
42
+ code = f.read()
43
+ self.analyze(code, file)
44
+ progress_bar.advance()
45
+
46
+ @staticmethod
47
+ def full_name(
48
+ filename: str, item_name: str | None = None, class_name: str | None = None
49
+ ) -> str:
50
+ if class_name is None:
51
+ if item_name is None:
52
+ return filename
53
+ return f"{filename}:{item_name}"
54
+ return f"{filename}:{class_name}:{item_name}"
55
+
56
+ def analyze(self, code: str, filename: str) -> None:
57
+ structure = self.ast_parser.extract_structure(code)
58
+
59
+ lizard_result = lizard.analyze_file(filename)
60
+ complexity_data = {
61
+ func.name: {
62
+ "complexity": func.cyclomatic_complexity,
63
+ "start_line": func.start_line,
64
+ "end_line": func.end_line,
65
+ }
66
+ for func in lizard_result.function_list
67
+ }
68
+
69
+ classes: dict[str, ClassNode] = {}
70
+ functions: dict[str, FunctionNode] = {}
71
+
72
+ for func_name in structure["functions"]:
73
+ full_name = self.full_name(filename, func_name)
74
+ try:
75
+ function_complexity_data = complexity_data[func_name]
76
+ except KeyError as e:
77
+ function_complexity_data = (
78
+ self.basic_complexity_analyzer.get_complexity(code, func_name)
79
+ )
80
+ if function_complexity_data is None:
81
+ print(
82
+ f"error for function {full_name}: no lizard complexity data, basic analyzer also failed: '{e}'"
83
+ )
84
+ continue
85
+ function_node = FunctionNode(
86
+ full_name,
87
+ func_name,
88
+ function_complexity_data["start_line"],
89
+ 0,
90
+ function_complexity_data["complexity"],
91
+ )
92
+ functions[full_name] = function_node
93
+
94
+ for class_name, method_names in structure["classes"].items():
95
+ full_name = self.full_name(filename, class_name)
96
+ class_node = ClassNode(
97
+ full_name,
98
+ class_name,
99
+ 0,
100
+ 0,
101
+ 0.0, # gets filled in later based on methods
102
+ )
103
+ classes[full_name] = class_node
104
+ for func_name in method_names:
105
+ full_name = self.full_name(filename, func_name, class_name)
106
+ try:
107
+ function_complexity_data = complexity_data[func_name]
108
+ except KeyError as e:
109
+ function_complexity_data = (
110
+ self.basic_complexity_analyzer.get_complexity(code, func_name)
111
+ )
112
+ if function_complexity_data is None:
113
+ print(
114
+ f"error for method {full_name}: no lizard complexity data, basic analyzer also failed: '{e}'"
115
+ )
116
+ continue
117
+ function_node = FunctionNode(
118
+ full_name,
119
+ func_name,
120
+ function_complexity_data["start_line"],
121
+ function_complexity_data["end_line"],
122
+ function_complexity_data["complexity"],
123
+ )
124
+ class_node.functions.append(function_node)
125
+ functions[full_name] = function_node
126
+
127
+ # complexity of classes
128
+ for class_node in classes.values():
129
+ class_node.real_complexity = sum(
130
+ [func.complexity for func in class_node.functions]
131
+ )
132
+
133
+ loc_data = TypescriptBasicLocAnalyzer.get_loc_metrics(code, filename)
134
+
135
+ full_name = self.full_name(filename)
136
+ module_node = ModuleNode(
137
+ full_name,
138
+ loc_data.get("lines", 0),
139
+ loc_data.get("linesOfCode", 0),
140
+ loc_data.get("logicalLinesOfCode", 0),
141
+ loc_data.get("commentLines", 0),
142
+ 0, # multi-line comments - not directly available
143
+ loc_data.get("linesOfCode", 0) - loc_data.get("logicalLinesOfCode", 0),
144
+ loc_data.get("commentLines", 0),
145
+ )
146
+ module_node.classes.extend(classes.values())
147
+ module_node.functions.extend(functions.values())
148
+
149
+ code_lines = code.split("\n")
150
+ for func_name, function_node in functions.items():
151
+ lines = code_lines[function_node.lineno : function_node.line_end]
152
+ function_metrics = self.halstead_analyzer.calculate_halstead_metrics(
153
+ "\n".join(lines)
154
+ )
155
+ function_node.h1 = function_metrics["n1"]
156
+ function_node.h2 = function_metrics["n2"]
157
+ function_node.N1 = function_metrics["N1"]
158
+ function_node.N2 = function_metrics["N2"]
159
+ function_node.vocabulary = function_metrics["vocabulary"]
160
+ function_node.length = function_metrics["length"]
161
+ function_node.volume = function_metrics["volume"]
162
+ function_node.difficulty = function_metrics["difficulty"]
163
+ function_node.effort = function_metrics["effort"]
164
+ function_node.calculated_length = function_metrics["calculated_length"]
165
+ function_node.bugs = function_metrics["bugs"]
166
+ function_node.time = function_metrics["time"]
167
+
168
+ maintainability_index = self._calculate_maintainability_index(
169
+ functions.values(), module_node
170
+ )
171
+ module_node.maintainability_index = maintainability_index
172
+ self.modules[full_name] = module_node
173
+
174
+ def _calculate_maintainability_index(
175
+ self, functions: list[FunctionNode], module_node: ModuleNode
176
+ ) -> float:
177
+ """Calculate maintainability index for PHP"""
178
+ if not functions:
179
+ return 100.0
180
+
181
+ total_volume = sum(func.volume for func in functions)
182
+ total_complexity = sum(func.complexity for func in functions)
183
+ total_length = sum(func.length for func in functions)
184
+
185
+ if total_volume == 0 or total_length == 0:
186
+ return 100.0
187
+
188
+ # PHP maintainability index calculation
189
+ mi_base = max(
190
+ (
191
+ 171
192
+ - 5.2 * math.log(total_volume)
193
+ - 0.23 * total_complexity
194
+ - 16.2 * math.log(total_length)
195
+ )
196
+ * 100
197
+ / 171,
198
+ 0,
199
+ )
200
+
201
+ # Comment weight
202
+ comment_weight = 0
203
+ if module_node.loc > 0:
204
+ comment_ratio = module_node.single_comments / module_node.loc
205
+ comment_weight = 50 * math.sin(math.sqrt(2.4 * comment_ratio))
206
+
207
+ return mi_base + comment_weight
208
+
209
+ def get_metrics(self):
210
+ return super().get_metrics()
@@ -0,0 +1,68 @@
1
+ from collections import defaultdict
2
+
3
+ from tree_sitter_languages import get_parser
4
+
5
+
6
+ class TypescriptAstParser:
7
+ def __init__(self):
8
+ self.parser = get_parser("typescript")
9
+
10
+ def _get_node_text(self, code: str, node) -> str:
11
+ return code[node.start_byte : node.end_byte].decode("utf-8")
12
+
13
+ def extract_structure(self, code: str) -> dict:
14
+ tree = self.parser.parse(bytes(code, "utf8"))
15
+ root_node = tree.root_node
16
+ structure = defaultdict(list)
17
+ structure["classes"] = {}
18
+ structure["functions"] = []
19
+ structure["enums"] = []
20
+
21
+ def traverse(node, parent_class=None):
22
+ if node.type == "class_declaration":
23
+ class_name = None
24
+ for child in node.children:
25
+ if child.type == "type_identifier":
26
+ class_name = self._get_node_text(code.encode(), child)
27
+ structure["classes"][class_name] = []
28
+ for child in node.children:
29
+ traverse(child, class_name)
30
+
31
+ elif node.type == "method_definition" and parent_class:
32
+ for child in node.children:
33
+ if child.type == "property_identifier":
34
+ method_name = self._get_node_text(code.encode(), child)
35
+ structure["classes"][parent_class].append(method_name)
36
+
37
+ elif node.type == "function_declaration":
38
+ for child in node.children:
39
+ if child.type == "identifier":
40
+ function_name = self._get_node_text(code.encode(), child)
41
+ structure["functions"].append(function_name)
42
+
43
+ elif node.type == "lexical_declaration":
44
+ # Handle exported arrow functions like: export const foo = (...) => {...}
45
+ for child in node.children:
46
+ if child.type == "variable_declarator":
47
+ identifier = None
48
+ for grandchild in child.children:
49
+ if grandchild.type == "identifier":
50
+ identifier = self._get_node_text(
51
+ code.encode(), grandchild
52
+ )
53
+ elif grandchild.type == "arrow_function":
54
+ if identifier:
55
+ structure["functions"].append(identifier)
56
+
57
+ elif node.type == "enum_declaration":
58
+ enum_name = None
59
+ for child in node.children:
60
+ if child.type == "identifier":
61
+ enum_name = self._get_node_text(code.encode(), child)
62
+ structure["enums"].append(enum_name)
63
+
64
+ for child in node.children:
65
+ traverse(child, parent_class)
66
+
67
+ traverse(root_node)
68
+ return dict(structure)
@@ -0,0 +1,114 @@
1
+ import re
2
+
3
+
4
+ class TypescriptBasicComplexityAnalzyer:
5
+ def get_complexity(self, code: str, function_name: str) -> dict[str, int] | None:
6
+ function_scope = self._extract_function(code, function_name)
7
+ if not function_scope:
8
+ return None
9
+ (start_index, end_index, start_line, end_line) = function_scope
10
+ function_code = code[start_index:end_index]
11
+
12
+ complexity = self._determine_complexity(function_code)
13
+
14
+ return {
15
+ "start_line": start_line,
16
+ "end_line": end_line,
17
+ "complexity": complexity,
18
+ }
19
+
20
+ def _determine_complexity(self, function_code: str) -> int:
21
+ complexity = 1
22
+
23
+ # Keywords that increase complexity
24
+ keywords = [
25
+ r"\bif\b",
26
+ r"\bfor\b",
27
+ r"\bwhile\b",
28
+ r"\bcase\b",
29
+ r"\bcatch\b",
30
+ r"\?\s*",
31
+ r"\belse\s+if\b",
32
+ r"\breturn\b.*\?",
33
+ r"\bthrow\b.*\?",
34
+ r"&&",
35
+ r"\|\|",
36
+ ]
37
+
38
+ for keyword in keywords:
39
+ matches = re.findall(keyword, function_code)
40
+ complexity += len(matches)
41
+
42
+ return complexity
43
+
44
+ def _extract_function(
45
+ self, code: str, function_name: str
46
+ ) -> tuple[int, int, int, int] | None:
47
+ # This pattern matches various JavaScript/TypeScript function declarations:
48
+ # 1. Arrow functions:
49
+ # - With or without `export`
50
+ # - With or without `async`
51
+ # - With or without generics (`<T>`)
52
+ # - With or without return types (`: Type`)
53
+ # - With multiline or single-line parameters
54
+ # - Including curried arrow functions (arrow returning another arrow)
55
+ # 2. Traditional functions:
56
+ # - With optional `export` and `async`
57
+ # 3. Class methods:
58
+ # - With optional access modifiers (`public`, `private`, `protected`)
59
+ # - With optional `static` and `async`
60
+ # - With optional generics and return types
61
+ # 4. Bare class methods (no modifiers)
62
+
63
+ pattern = re.compile(
64
+ rf"""
65
+ (
66
+ (export\s+)?(const|let|var)\s+{re.escape(function_name)}\s*
67
+ (<[^>]*>)?\s*=\s*(async\s+)?\(\s*.*?\s*\)\s*
68
+ (:\s*[^=]+)?\s*=>\s*
69
+ (\s*\(\s*.*?\s*\)\s*=>)?\s*\{{?
70
+ )
71
+ |
72
+ (
73
+ (export\s+)?(async\s+)?function\s+{re.escape(function_name)}\s*
74
+ \(\s*.*?\s*\)\s*\{{
75
+ )
76
+ |
77
+ (
78
+ (?:public|private|protected)?\s*
79
+ (?:static\s+)?(?:async\s+)?{re.escape(function_name)}\s*
80
+ (<[^>]+>)?\s*\(\s*.*?\s*\)\s*
81
+ (:\s*[^\{{]+)?\s*\{{
82
+ )
83
+ |
84
+ (
85
+ {re.escape(function_name)}\s*\(\s*.*?\s*\)\s*\{{
86
+ )
87
+ """,
88
+ re.MULTILINE | re.DOTALL | re.VERBOSE,
89
+ )
90
+
91
+ match = pattern.search(code)
92
+ if not match:
93
+ print(f"Function '{function_name}' not found.")
94
+ return None
95
+
96
+ start_index = match.start()
97
+ start_line = code[:start_index].count("\n") + 1
98
+
99
+ # Find the matching closing brace
100
+ brace_count = 0
101
+ i = match.end() - 1
102
+ while i < len(code):
103
+ if code[i] == "{":
104
+ brace_count += 1
105
+ elif code[i] == "}":
106
+ brace_count -= 1
107
+ if brace_count == 0:
108
+ end_index = i + 1
109
+ end_line = code[:end_index].count("\n") + 1
110
+ return (start_index, end_index, start_line, end_line)
111
+ i += 1
112
+
113
+ print(f"Function '{function_name}' seems to be incomplete or malformed.")
114
+ return None
@@ -0,0 +1,69 @@
1
+ class TypescriptBasicLocAnalyzer:
2
+ @staticmethod
3
+ def get_loc_metrics(code: str, filename: str) -> dict:
4
+ """Fallback LOC calculation using manual analysis"""
5
+ try:
6
+ lines = code.split("\n")
7
+
8
+ total_lines = len(lines)
9
+ blank_lines = TypescriptBasicLocAnalyzer._count_blank_lines(lines)
10
+ comment_lines = TypescriptBasicLocAnalyzer._count_comment_lines(lines)
11
+ code_lines = total_lines - blank_lines - comment_lines
12
+
13
+ return {
14
+ "lines": total_lines,
15
+ "linesOfCode": code_lines,
16
+ "logicalLinesOfCode": code_lines,
17
+ "commentLines": comment_lines,
18
+ "blankLines": blank_lines,
19
+ }
20
+ except Exception as e:
21
+ print(f"Fallback LOC analysis failed: {e}")
22
+ return {
23
+ "lines": 0,
24
+ "linesOfCode": 0,
25
+ "logicalLinesOfCode": 0,
26
+ "commentLines": 0,
27
+ "blankLines": 0,
28
+ }
29
+
30
+ @staticmethod
31
+ def _count_blank_lines(lines: list) -> int:
32
+ """Count blank lines"""
33
+ return sum(1 for line in lines if not line.strip())
34
+
35
+ @staticmethod
36
+ def _count_comment_lines(lines: list) -> int:
37
+ """Count comment lines (single-line and multi-line)"""
38
+ comment_count = 0
39
+ in_multiline_comment = False
40
+
41
+ for line in lines:
42
+ stripped = line.strip()
43
+
44
+ # Handle multi-line comments
45
+ if "/*" in stripped:
46
+ in_multiline_comment = True
47
+ comment_count += 1
48
+ # Check if comment closes on same line
49
+ if "*/" in stripped:
50
+ in_multiline_comment = False
51
+ continue
52
+
53
+ if in_multiline_comment:
54
+ comment_count += 1
55
+ if "*/" in stripped:
56
+ in_multiline_comment = False
57
+ continue
58
+
59
+ # Handle single-line comments
60
+ if stripped.startswith(("//")) or stripped.startswith("#"):
61
+ comment_count += 1
62
+ continue
63
+
64
+ # Handle doc comments
65
+ if stripped.startswith("*") and not stripped.startswith("*/"):
66
+ comment_count += 1
67
+ continue
68
+
69
+ return comment_count
@@ -0,0 +1,55 @@
1
+ from LangAnalyzer.Generic.HalSteadAnalyzer import HalSteadAnalyzer
2
+
3
+
4
+ class TypeScriptHalSteadAnalyzer:
5
+ def __init__(self):
6
+ self.operators = set(
7
+ [
8
+ "+",
9
+ "-",
10
+ "*",
11
+ "/",
12
+ "%",
13
+ "++",
14
+ "--",
15
+ "==",
16
+ "!=",
17
+ "===",
18
+ "!==",
19
+ "<",
20
+ ">",
21
+ "<=",
22
+ ">=",
23
+ "&&",
24
+ "||",
25
+ "!",
26
+ "=",
27
+ "+=",
28
+ "-=",
29
+ "*=",
30
+ "/=",
31
+ "%=",
32
+ "&",
33
+ "|",
34
+ "^",
35
+ "~",
36
+ "<<",
37
+ ">>",
38
+ "?",
39
+ ":",
40
+ ".",
41
+ ",",
42
+ ";",
43
+ "=>",
44
+ "(",
45
+ ")",
46
+ "[",
47
+ "]",
48
+ "{",
49
+ "}",
50
+ ]
51
+ )
52
+ self.analyzer = HalSteadAnalyzer(self.operators)
53
+
54
+ def calculate_halstead_metrics(self, code: str):
55
+ return self.analyzer.calculate_halstead_metrics(code)
File without changes
@@ -0,0 +1,42 @@
1
+ from Metric.Code.SegmentedMetrics import SegmentedMetrics
2
+
3
+
4
+ class AggregatedMetrics:
5
+ """Used to show aggregated metrics on the index page"""
6
+
7
+ def __init__(
8
+ self,
9
+ loc: int = 0,
10
+ avgCcPerFunction: float = 0.0,
11
+ maintainabilityIndex: float = 0.0,
12
+ avgLocPerFunction: float = 0.0,
13
+ num_files: int = 0,
14
+ segmented_loc: SegmentedMetrics = SegmentedMetrics(),
15
+ segmented_complexity: SegmentedMetrics = SegmentedMetrics(),
16
+ segmented_maintainability: SegmentedMetrics = SegmentedMetrics(),
17
+ segmented_method_size: SegmentedMetrics = SegmentedMetrics(),
18
+ ) -> None:
19
+ self.loc = loc
20
+ self.avgCcPerFunction = avgCcPerFunction
21
+ self.maintainabilityIndex = maintainabilityIndex
22
+ self.avgLocPerFunction = avgLocPerFunction
23
+ self.num_files = num_files
24
+
25
+ self.segmentation_data = {
26
+ "loc": segmented_loc,
27
+ "complexity": segmented_complexity,
28
+ "maintainability": segmented_maintainability,
29
+ "methodSize": segmented_method_size,
30
+ }
31
+
32
+ def to_dict(self) -> dict:
33
+ return {
34
+ "loc": str(self.loc),
35
+ "avgCcPerFunction": f"{self.avgCcPerFunction:.2f}",
36
+ "maintainabilityIndex": f"{self.maintainabilityIndex:.2f}",
37
+ "avgLocPerFunction": f"{self.avgLocPerFunction:.2f}",
38
+ "num_files": str(self.num_files),
39
+ }
40
+
41
+ def to_dict_segmentation(self) -> dict:
42
+ return {k: v.to_dict() for k, v in self.segmentation_data.items()}
@@ -0,0 +1,33 @@
1
+ from Tree.ClassNode import ClassNode
2
+ from Tree.FunctionNode import FunctionNode
3
+
4
+
5
+ class FileMetrics:
6
+ def __init__(
7
+ self,
8
+ full_name: str,
9
+ loc: int,
10
+ avgCcPerFunction: float,
11
+ maintainabilityIndex: float,
12
+ avgLocPerFunction: float,
13
+ class_nodes: list[ClassNode],
14
+ function_nodes: list[FunctionNode],
15
+ ):
16
+ self.full_name = full_name
17
+ self.loc = loc
18
+ self.avgCcPerFunction = avgCcPerFunction
19
+ self.maintainabilityIndex = maintainabilityIndex
20
+ self.avgLocPerFunction = avgLocPerFunction
21
+ self.class_nodes = class_nodes
22
+ self.function_nodes = function_nodes
23
+
24
+ def to_dict(self) -> dict:
25
+ return {
26
+ "full_name": self.full_name,
27
+ "loc": self.loc,
28
+ "avgCcPerFunction": self.avgCcPerFunction,
29
+ "maintainabilityIndex": f"{self.maintainabilityIndex:.2f}",
30
+ "avgLocPerFunction": self.avgLocPerFunction,
31
+ "class_nodes": [node.to_dict() for node in self.class_nodes],
32
+ "function_nodes": [node.to_dict() for node in self.function_nodes],
33
+ }
@@ -0,0 +1,32 @@
1
+ from Tree.ClassNode import ClassNode
2
+ from Tree.FunctionNode import FunctionNode
3
+
4
+
5
+ class ModuleMetrics:
6
+ def __init__(
7
+ self,
8
+ loc: int,
9
+ avgCcPerFunction: float,
10
+ maintainabilityIndex: float,
11
+ avgLocPerFunction: float,
12
+ class_nodes: list[ClassNode],
13
+ function_nodes: list[FunctionNode],
14
+ ):
15
+ self.loc = loc
16
+ self.avgCcPerFunction = avgCcPerFunction
17
+ self.maintainabilityIndex = maintainabilityIndex
18
+ self.avgLocPerFunction = avgLocPerFunction
19
+ self.num_files = 1
20
+ self.class_nodes: list[ClassNode] = class_nodes
21
+ self.function_nodes: list[FunctionNode] = function_nodes
22
+
23
+ def to_dict(self) -> dict:
24
+ return {
25
+ "loc": str(self.loc),
26
+ "avgCcPerFunction": f"{self.avgCcPerFunction:.2f}",
27
+ "maintainabilityIndex": f"{self.maintainabilityIndex:.2f}",
28
+ "avgLocPerFunction": f"{self.avgLocPerFunction:.2f}",
29
+ "num_files": str(self.num_files),
30
+ "class_nodes": [node.to_dict() for node in self.class_nodes],
31
+ "function_nodes": [node.to_dict() for node in self.function_nodes],
32
+ }