metripy 0.2.8__py3-none-any.whl → 0.3.1__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 (56) 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 +36 -0
  11. metripy/Dependency/Dependency.py +2 -1
  12. metripy/Dependency/Pip/Pip.py +1 -2
  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 +17 -2
  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 +40 -0
  39. metripy/Report/Html/PageRenderer.py +43 -0
  40. metripy/Report/Html/PageRendererFactory.py +37 -0
  41. metripy/Report/Html/Reporter.py +49 -130
  42. metripy/Report/Html/TopOffendersPageRenderer.py +84 -0
  43. metripy/Report/Html/TrendsPageRenderer.py +114 -0
  44. metripy/Report/Json/GitJsonReporter.py +3 -1
  45. metripy/Report/Json/JsonReporter.py +4 -1
  46. metripy/Report/ReporterFactory.py +4 -2
  47. metripy/Tree/ClassNode.py +21 -0
  48. metripy/Tree/FunctionNode.py +66 -1
  49. metripy/Trend/TrendAnalyzer.py +150 -0
  50. {metripy-0.2.8.dist-info → metripy-0.3.1.dist-info}/METADATA +3 -3
  51. metripy-0.3.1.dist-info/RECORD +85 -0
  52. metripy-0.2.8.dist-info/RECORD +0 -66
  53. {metripy-0.2.8.dist-info → metripy-0.3.1.dist-info}/WHEEL +0 -0
  54. {metripy-0.2.8.dist-info → metripy-0.3.1.dist-info}/entry_points.txt +0 -0
  55. {metripy-0.2.8.dist-info → metripy-0.3.1.dist-info}/licenses/LICENSE +0 -0
  56. {metripy-0.2.8.dist-info → metripy-0.3.1.dist-info}/top_level.txt +0 -0
@@ -7,14 +7,15 @@ from metripy.Dependency.Dependency import Dependency
7
7
  from metripy.Dependency.Npm.Npm import Npm
8
8
  from metripy.Dependency.Pip.Pip import Pip
9
9
  from metripy.Git.GitAnalyzer import GitAnalyzer
10
+ from metripy.Import.Json.JsonImporter import JsonImporter
10
11
  from metripy.LangAnalyzer.AbstractLangAnalyzer import AbstractLangAnalyzer
11
12
  from metripy.LangAnalyzer.Php.PhpAnalyzer import PhpAnalyzer
12
13
  from metripy.LangAnalyzer.Python.PythonAnalyzer import PythonAnalyzer
13
- from metripy.LangAnalyzer.Typescript.TypescriptAnalyzer import \
14
- TypescriptAnalyzer
14
+ from metripy.LangAnalyzer.Typescript.TypescriptAnalyzer import TypescriptAnalyzer
15
15
  from metripy.Metric.Code.FileMetrics import FileMetrics
16
16
  from metripy.Metric.Git.GitMetrics import GitMetrics
17
17
  from metripy.Metric.ProjectMetrics import ProjectMetrics
18
+ from metripy.Trend.TrendAnalyzer import TrendAnalyzer
18
19
 
19
20
 
20
21
  class Analyzer:
@@ -85,6 +86,18 @@ class Analyzer:
85
86
 
86
87
  return dependencies
87
88
 
89
+ def add_trends(self, project_metrics: ProjectMetrics):
90
+ self.output.writeln("<info>Analyzing trends...</info>")
91
+ importer = JsonImporter(self.output)
92
+ historical_project_metrics = importer.import_data(self.config.history_path)
93
+ TrendAnalyzer().add_historical_file_trends(
94
+ project_metrics.file_metrics, historical_project_metrics.file_metrics
95
+ )
96
+ TrendAnalyzer().add_historical_project_trends(
97
+ project_metrics, historical_project_metrics
98
+ )
99
+ self.output.writeln("<success>Trends analyzed</success>")
100
+
88
101
  def run(self, files: list[str]) -> ProjectMetrics:
89
102
  git_stats = None
90
103
  if self.config.git:
@@ -103,4 +116,11 @@ class Analyzer:
103
116
  elif self.config.npm:
104
117
  packages = self.analyze_npm()
105
118
 
106
- return ProjectMetrics(file_metrics, git_stats, packages)
119
+ if not self.config.history_path:
120
+ return ProjectMetrics(file_metrics, git_stats, packages)
121
+
122
+ # analyze trends
123
+ project_metrics = ProjectMetrics(file_metrics, git_stats, packages)
124
+ self.add_trends(project_metrics)
125
+
126
+ return project_metrics
@@ -2,6 +2,7 @@ import json
2
2
 
3
3
  from metripy.Application.Analyzer import Analyzer
4
4
  from metripy.Application.Config.Parser import Parser
5
+ from metripy.Application.Info import Info
5
6
  from metripy.Component.Debug.Debugger import Debugger
6
7
  from metripy.Component.File.Finder import Finder
7
8
  from metripy.Component.Output.CliOutput import CliOutput
@@ -14,9 +15,22 @@ class Application:
14
15
  output = CliOutput()
15
16
 
16
17
  # issues and debug
17
- debugger = Debugger(output).enable()
18
+ debugger = Debugger(output)
18
19
 
19
20
  config = Parser().parse(argv)
21
+ if config.debug:
22
+ debugger.enable()
23
+
24
+ if config.version:
25
+ output.writeln(Info().get_version_info())
26
+ return
27
+
28
+ if config.help:
29
+ output.writeln(Info().get_help())
30
+ return
31
+
32
+ if config.quiet:
33
+ output.set_quiet(True)
20
34
 
21
35
  finder = Finder()
22
36
  files = finder.fetch(config.project_configs)
@@ -46,7 +60,7 @@ class Application:
46
60
  )
47
61
  for report_config in project_config.reports:
48
62
  reporter: ReporterInterface = ReporterFactory.create(
49
- report_config, output
63
+ report_config, output, project_config.name
50
64
  )
51
65
  reporter.generate(project_metrics)
52
66
  output.writeln(
@@ -4,6 +4,10 @@ from metripy.Application.Config.ProjectConfig import ProjectConfig
4
4
  class Config:
5
5
  def __init__(self):
6
6
  self.project_configs: list[ProjectConfig] = []
7
+ self.quiet: bool = False
8
+ self.version: bool = False
9
+ self.help: bool = False
10
+ self.debug: bool = False
7
11
 
8
12
  def to_dict(self) -> dict:
9
13
  return {
@@ -11,3 +15,33 @@ class Config:
11
15
  project_config.to_dict() for project_config in self.project_configs
12
16
  ],
13
17
  }
18
+
19
+ def set(self, param: str, value: any) -> None:
20
+ if param == "quiet":
21
+ self.quiet = value
22
+ elif param == "version":
23
+ self.version = value
24
+ elif param == "help":
25
+ self.help = value
26
+ elif param == "debug":
27
+ self.debug = value
28
+ elif param.startswith("configs."):
29
+ self._set_project_value(param[len("configs.") :], value)
30
+ else:
31
+ # ignore unknown parameters
32
+ return
33
+
34
+ def _set_project_value(self, param: str, value: any) -> None:
35
+ keys = param.split(".")
36
+ project_name = keys[0]
37
+ project_config = next(
38
+ (pc for pc in self.project_configs if pc.name == project_name), None
39
+ )
40
+ if not project_config:
41
+ project_config = ProjectConfig(project_name)
42
+ self.project_configs.append(project_config)
43
+ if len(keys) > 1:
44
+ project_config.set(keys[1:], value)
45
+ else:
46
+ # weird but okay
47
+ return
@@ -1,10 +1,11 @@
1
1
  import os
2
2
  import pathlib
3
3
 
4
- from metripy.Application.Config.File.ConfigFileReaderInterface import \
5
- ConfigFileReaderInterface
6
- from metripy.Application.Config.File.JsonConfigFileReader import \
7
- JsonConfigFileReader
4
+ from metripy.Application.Config.File.ConfigFileReaderInterface import (
5
+ ConfigFileReaderInterface,
6
+ )
7
+ from metripy.Application.Config.File.JsonConfigFileReader import JsonConfigFileReader
8
+ from metripy.Application.Config.File.YamlConfigFileReader import YamlConfigFileReader
8
9
 
9
10
 
10
11
  class ConfigFileReaderFactory:
@@ -17,7 +18,7 @@ class ConfigFileReaderFactory:
17
18
  if extension == ".json":
18
19
  return JsonConfigFileReader(filename)
19
20
  elif extension == ".yaml" or extension == ".yml":
20
- raise NotImplementedError("YAML support is not implemented yet")
21
+ return YamlConfigFileReader(filename)
21
22
  elif extension == ".xml":
22
23
  raise NotImplementedError("XML support is not implemented yet")
23
24
  else:
@@ -1,14 +1,81 @@
1
+ import os
1
2
  from abc import ABC, abstractmethod
2
3
 
3
4
  from metripy.Application.Config.Config import Config
5
+ from metripy.Application.Config.GitConfig import GitConfig
6
+ from metripy.Application.Config.ProjectConfig import ProjectConfig
7
+ from metripy.Application.Config.ReportConfig import ReportConfig
4
8
 
5
9
 
6
10
  class ConfigFileReaderInterface(ABC):
7
-
8
- @abstractmethod
9
11
  def __init__(self, filename: str):
10
- pass
12
+ self.filename = filename
11
13
 
12
14
  @abstractmethod
13
15
  def read(self, config: Config) -> None:
14
16
  pass
17
+
18
+ def resolve_path(self, path: str) -> str:
19
+ return os.path.join(os.path.dirname(self.filename), path)
20
+
21
+ def parse_data(self, data: dict, config: Config) -> None:
22
+ # configs
23
+ if configs := data.get("configs"):
24
+ for project_name, project_config in configs.items():
25
+ project_config = self.parse_config_json(project_name, project_config)
26
+ config.project_configs.append(project_config)
27
+
28
+ def parse_config_json(self, project_name: str, data: dict) -> ProjectConfig:
29
+ project_config = ProjectConfig(project_name)
30
+
31
+ # extensions
32
+ if base_path := data.get("base_path"):
33
+ project_config.base_path = base_path
34
+
35
+ # includes
36
+ if includes := data.get("includes"):
37
+ files = []
38
+ # with config file, includes are relative to the config file
39
+ for include in includes:
40
+ include = self.resolve_path(include)
41
+ files.append(include)
42
+
43
+ project_config.includes = files
44
+
45
+ # extensions
46
+ if extensions := data.get("extensions"):
47
+ project_config.extensions = extensions
48
+
49
+ # excludes
50
+ if excludes := data.get("excludes"):
51
+ project_config.excludes = excludes
52
+
53
+ # reports
54
+ if reports := data.get("reports"):
55
+ for report_type, path in reports.items():
56
+ path = self.resolve_path(path)
57
+ project_config.reports.append(ReportConfig(report_type, path))
58
+
59
+ # git
60
+ if git := data.get("git"):
61
+ project_config.git = GitConfig()
62
+ project_config.git.repo = project_config.base_path
63
+ project_config.git.branch = git.get("branch", project_config.git.branch)
64
+
65
+ # composer
66
+ if composer := data.get("composer"):
67
+ project_config.composer = composer
68
+
69
+ # pip
70
+ if pip := data.get("pip"):
71
+ project_config.pip = pip
72
+
73
+ # npm
74
+ if npm := data.get("npm"):
75
+ project_config.npm = npm
76
+
77
+ # trends
78
+ if history_path := data.get("trends"):
79
+ project_config.history_path = self.resolve_path(history_path)
80
+
81
+ return project_config
@@ -1,82 +1,17 @@
1
1
  import json
2
- import os
3
2
 
4
3
  from metripy.Application.Config.Config import Config
5
- from metripy.Application.Config.File.ConfigFileReaderInterface import \
6
- ConfigFileReaderInterface
7
- from metripy.Application.Config.GitConfig import GitConfig
8
- from metripy.Application.Config.ProjectConfig import ProjectConfig
9
- from metripy.Application.Config.ReportConfig import ReportConfig
4
+ from metripy.Application.Config.File.ConfigFileReaderInterface import (
5
+ ConfigFileReaderInterface,
6
+ )
10
7
 
11
8
 
12
9
  class JsonConfigFileReader(ConfigFileReaderInterface):
13
10
  def __init__(self, filename: str):
14
- self.filename = filename
11
+ super().__init__(filename)
15
12
 
16
13
  def read(self, config: Config) -> None:
17
14
  with open(self.filename, "r") as file:
18
15
  json_data = json.load(file)
19
16
 
20
- self.parse_json(json_data, config)
21
-
22
- def resolve_path(self, path: str) -> str:
23
- return os.path.join(os.path.dirname(self.filename), path)
24
-
25
- def parse_json(self, json_data: dict, config: Config) -> None:
26
-
27
- # configs
28
- if configs := json_data.get("configs"):
29
- for project_name, project_config in configs.items():
30
- project_config = self.parse_config_json(project_name, project_config)
31
- config.project_configs.append(project_config)
32
-
33
- def parse_config_json(self, project_name: str, json_data: dict) -> ProjectConfig:
34
- project_config = ProjectConfig(project_name)
35
-
36
- # extensions
37
- if base_path := json_data.get("base_path"):
38
- project_config.base_path = base_path
39
-
40
- # includes
41
- if includes := json_data.get("includes"):
42
- files = []
43
- # with config file, includes are relative to the config file
44
- for include in includes:
45
- include = self.resolve_path(include)
46
- files.append(include)
47
-
48
- project_config.includes = files
49
-
50
- # extensions
51
- if extensions := json_data.get("extensions"):
52
- project_config.extensions = extensions
53
-
54
- # excludes
55
- if excludes := json_data.get("excludes"):
56
- project_config.excludes = excludes
57
-
58
- # reports
59
- if reports := json_data.get("reports"):
60
- for report_type, path in reports.items():
61
- path = self.resolve_path(path)
62
- project_config.reports.append(ReportConfig(report_type, path))
63
-
64
- # git
65
- if git := json_data.get("git"):
66
- project_config.git = GitConfig()
67
- project_config.git.repo = project_config.base_path
68
- project_config.git.branch = git.get("branch", project_config.git.branch)
69
-
70
- # composer
71
- if composer := json_data.get("composer"):
72
- project_config.composer = composer
73
-
74
- # pip
75
- if pip := json_data.get("pip"):
76
- project_config.pip = pip
77
-
78
- # npm
79
- if npm := json_data.get("npm"):
80
- project_config.npm = npm
81
-
82
- return project_config
17
+ self.parse_data(json_data, config)
@@ -0,0 +1,17 @@
1
+ import yaml
2
+
3
+ from metripy.Application.Config.Config import Config
4
+ from metripy.Application.Config.File.ConfigFileReaderInterface import (
5
+ ConfigFileReaderInterface,
6
+ )
7
+
8
+
9
+ class YamlConfigFileReader(ConfigFileReaderInterface):
10
+ def __init__(self, filename: str):
11
+ super().__init__(filename)
12
+
13
+ def read(self, config: Config) -> None:
14
+ with open(self.filename, "r") as file:
15
+ yaml_data = yaml.safe_load(file)
16
+
17
+ self.parse_data(yaml_data, config)
@@ -1,21 +1,22 @@
1
1
  import re
2
2
 
3
3
  from metripy.Application.Config.Config import Config
4
- from metripy.Application.Config.File.ConfigFileReaderFactory import \
5
- ConfigFileReaderFactory
4
+ from metripy.Application.Config.File.ConfigFileReaderFactory import (
5
+ ConfigFileReaderFactory,
6
+ )
6
7
 
7
8
 
8
9
  class Parser:
9
10
  def parse(self, argv: list[str]) -> Config:
10
11
  config = Config()
11
12
 
12
- if argv[0] == "metripy.py" or argv[0] == "metripy":
13
- # TODO, fix when path ends with metripy.py
14
- pass
15
- argv.pop(0)
13
+ if argv[0].endswith("metripy.py") or argv[0].endswith("metripy"):
14
+ argv.pop(0)
16
15
 
17
16
  # check for a config file
18
- for key, arg in enumerate(argv):
17
+ key = 0
18
+ while key < len(argv):
19
+ arg = argv[key]
19
20
  if matches := re.search(r"^--config=(.+)$", arg):
20
21
  fileReader = ConfigFileReaderFactory.createFromFileName(
21
22
  matches.group(1)
@@ -23,9 +24,21 @@ class Parser:
23
24
  fileReader.read(config)
24
25
  argv.pop(key)
25
26
 
26
- # TODO: add the following
27
- # arguments with options
28
- # arguments without options
29
- # last argument
27
+ # arguments with options
28
+ elif matches := re.search(r"^--([\w]+(?:\.[\w]+)*)=(.*)$", arg):
29
+ param = matches.group(1)
30
+ value = matches.group(2)
31
+ config.set(param, value)
32
+ argv.pop(key)
33
+
34
+ # arguments without options
35
+ elif matches := re.search(r"^--([\w]+(?:\.[\w]+)*)$", arg):
36
+ param = matches.group(1)
37
+ config.set(param, True)
38
+ argv.pop(key)
39
+ else:
40
+ key += 1
41
+
42
+ # TODO handle remaining arguments
30
43
 
31
44
  return config
@@ -14,6 +14,7 @@ class ProjectConfig:
14
14
  self.pip: bool = False
15
15
  self.npm: bool = False
16
16
  self.reports: list[ReportConfig] = []
17
+ self.history_path: str | None = None
17
18
 
18
19
  def to_dict(self) -> dict:
19
20
  return {
@@ -22,6 +23,69 @@ class ProjectConfig:
22
23
  "includes": self.includes,
23
24
  "excludes": self.excludes,
24
25
  "extensions": self.extensions,
26
+ "composer": self.composer,
27
+ "pip": self.pip,
28
+ "npm": self.npm,
25
29
  "git": self.git.to_dict() if self.git else None,
26
30
  "reports": [report.to_dict() for report in self.reports],
31
+ "history_path": self.history_path,
27
32
  }
33
+
34
+ @staticmethod
35
+ def str_to_bool(value):
36
+ if isinstance(value, bool):
37
+ return value
38
+ return str(value).lower() in ("true", "1", "yes")
39
+
40
+ def set(self, keys: list[str], value: any) -> None:
41
+ if len(keys) == 0:
42
+ return
43
+ primary_key = keys[0]
44
+ # single value
45
+ if primary_key == "base_path":
46
+ self.base_path = value
47
+ elif primary_key == "pip":
48
+ self.pip = self.str_to_bool(value)
49
+ elif primary_key == "npm":
50
+ self.npm = self.str_to_bool(value)
51
+ elif primary_key == "composer":
52
+ self.composer = self.str_to_bool(value)
53
+ elif primary_key == "trends":
54
+ self.history_path = value
55
+ elif primary_key == "git":
56
+ self.git = GitConfig()
57
+ self.git.repo = self.base_path
58
+ self.git.branch = value
59
+
60
+ # list values
61
+ elif primary_key == "includes":
62
+ if value == "":
63
+ self.includes = []
64
+ else:
65
+ self.includes.append(value)
66
+ elif primary_key == "excludes":
67
+ if value == "":
68
+ self.excludes = []
69
+ else:
70
+ self.excludes.append(value)
71
+ elif primary_key == "extensions":
72
+ if value == "":
73
+ self.extensions = []
74
+ else:
75
+ self.extensions.append(value)
76
+
77
+ # dict values
78
+ elif primary_key == "reports":
79
+ if len(keys) == 1:
80
+ return
81
+ report_type = keys[1]
82
+ report_path = value
83
+ if value != "":
84
+ self.reports.append(ReportConfig(report_type, report_path))
85
+ else:
86
+ report_config = next(
87
+ (rc for rc in self.reports if rc.type == report_type), None
88
+ )
89
+ if not report_config:
90
+ return
91
+ self.reports.remove(report_config)
@@ -0,0 +1,36 @@
1
+ import toml
2
+
3
+
4
+ class Info:
5
+ def __init__(self):
6
+ data = self._get_data()
7
+ self.version = data["project"]["version"]
8
+ self.url = data["project"]["urls"]["Homepage"]
9
+
10
+ def _get_data(self) -> dict:
11
+ with open("pyproject.toml", "r") as file:
12
+ data = toml.load(file)
13
+ return data
14
+
15
+ def get_version(self) -> str:
16
+ return self.version
17
+
18
+ def get_version_info(self) -> str:
19
+ return f"""
20
+ Metripy {self.get_version()}
21
+ {self.url}
22
+ """
23
+
24
+ def get_help(self) -> str:
25
+ return (
26
+ self.get_version_info()
27
+ + """
28
+ Usage: metripy [options]
29
+ Options:
30
+ --config=<file> Use a custom config file
31
+ --version Show the version and exit
32
+ --help Show this help message and exit
33
+ --debug Enable debug mode
34
+ --quiet Disable output
35
+ """
36
+ )
@@ -1,7 +1,8 @@
1
1
  from typing import Self
2
2
 
3
+
3
4
  class Dependency:
4
- def __init__(self, name: str, version: str|None):
5
+ def __init__(self, name: str, version: str | None):
5
6
  self.name = name
6
7
  self.version = version
7
8
  self.latest: str = ""
@@ -1,5 +1,4 @@
1
1
  import os
2
- import re
3
2
 
4
3
  import toml
5
4
 
@@ -39,7 +38,7 @@ class Pip:
39
38
  return self._parse_dependencies(deps)
40
39
 
41
40
  return []
42
-
41
+
43
42
  def _parse_dependencies(self, lines: list[str]) -> list[Dependency]:
44
43
  dependencies = []
45
44
  for dep in lines:
@@ -16,6 +16,7 @@ class PyPi:
16
16
  print(f"Package '{dependency.name}' has no info section")
17
17
  return dependency
18
18
 
19
+ dependency.type = "pip"
19
20
  dependency.description = info.get("summary")
20
21
  dependency.repository = info.get("project_url") or info.get("home_page")
21
22
  if info.get("license"):
@@ -9,8 +9,6 @@ from metripy.Metric.Git.GitMetrics import GitMetrics
9
9
 
10
10
  class GitAnalyzer:
11
11
  def __init__(self, git_config: GitConfig):
12
- print(git_config.repo)
13
- print(git_config.branch)
14
12
  self.repo = Repo(git_config.repo)
15
13
  self.branch_name = git_config.branch
16
14
 
@@ -22,7 +20,6 @@ class GitAnalyzer:
22
20
  first_of_month_last_year = datetime(now.year - 1, now.month, 1)
23
21
  # first_of_month_last_year = datetime(now.year, now.month, 1)
24
22
  after_date = first_of_month_last_year.strftime("%Y-%m-%d")
25
- print(f"analyzing from {after_date}")
26
23
 
27
24
  return self.get_metrics(after_date)
28
25
 
@@ -0,0 +1,17 @@
1
+ import json
2
+
3
+ from metripy.Component.Output.CliOutput import CliOutput
4
+ from metripy.Metric.ProjectMetrics import ProjectMetrics
5
+
6
+
7
+ class JsonImporter:
8
+ def __init__(self, output: CliOutput):
9
+ self.output = output
10
+
11
+ def import_data(self, path: str) -> ProjectMetrics:
12
+ self.output.writeln(f"<info>Importing data from {path}...</info>")
13
+ with open(path, "r") as file:
14
+ data = json.load(file)
15
+ project_metrics = ProjectMetrics.from_dict(data)
16
+ self.output.writeln("<success>Data imported successfuly</success>")
17
+ return project_metrics
@@ -40,13 +40,13 @@ class AbstractLangAnalyzer(ABC):
40
40
  full_name = module.full_name
41
41
 
42
42
  if len(module.functions) > 0:
43
- avgCcPerFunction = sum(
44
- function.complexity for function in module.functions
45
- ) / len(module.functions)
43
+ totalCc = sum(function.complexity for function in module.functions)
44
+ avgCcPerFunction = totalCc / len(module.functions)
46
45
  avgLocPerFunction = (
47
46
  module.lloc - module.comments - len(module.functions)
48
47
  ) / len(module.functions)
49
48
  else:
49
+ totalCc = 0
50
50
  avgCcPerFunction = 0
51
51
  avgLocPerFunction = 0
52
52
  maintainabilityIndex = module.maintainability_index
@@ -54,6 +54,7 @@ class AbstractLangAnalyzer(ABC):
54
54
  file_metric = FileMetrics(
55
55
  full_name=full_name,
56
56
  loc=module.loc,
57
+ totalCc=totalCc,
57
58
  avgCcPerFunction=avgCcPerFunction,
58
59
  maintainabilityIndex=maintainabilityIndex,
59
60
  avgLocPerFunction=avgLocPerFunction,
@@ -131,7 +131,7 @@ class PhpAnalyzer(AbstractLangAnalyzer):
131
131
 
132
132
  code_lines = code.split("\n")
133
133
  for func_name, function_node in functions.items():
134
- lines = code_lines[function_node.lineno:function_node.line_end]
134
+ lines = code_lines[function_node.lineno : function_node.line_end]
135
135
  function_metrics = self.halstead_analyzer.calculate_halstead_metrics(
136
136
  "\n".join(lines)
137
137
  )
@@ -147,6 +147,7 @@ class PhpAnalyzer(AbstractLangAnalyzer):
147
147
  function_node.calculated_length = function_metrics["calculated_length"]
148
148
  function_node.bugs = function_metrics["bugs"]
149
149
  function_node.time = function_metrics["time"]
150
+ function_node.calc_mi()
150
151
 
151
152
  maintainability_index = self._calculate_maintainability_index(
152
153
  functions.values(), module_node