Source code for projectreport.analyzer.analysis
from typing import TYPE_CHECKING, List, Optional
if TYPE_CHECKING:
from projectreport.analyzer.module import Module
from projectreport.analyzer.analyzable import Analyzable
from projectreport.analyzer.folder import Folder
from datetime import datetime
import pygount
from cached_property import cached_property
from git import Commit # type: ignore
[docs]class Analysis:
_loc: Optional[int] = None
_full_loc: Optional[int] = None
[docs] def __init__(self, analyzable: "Analyzable"):
self.git_analysis = GitAnalysis(analyzable)
@cached_property
def data(self):
return dict(
num_commits=self.git_analysis.num_commits,
created=self.git_analysis.created,
updated=self.git_analysis.updated,
lines=self.loc,
full_lines=self.full_loc,
urls=self.git_analysis.urls,
)
@property
def loc(self) -> Optional[int]:
return self._loc
@loc.setter
def loc(self, value: int):
self._loc = value
@property
def full_loc(self) -> Optional[int]:
return self._full_loc
@full_loc.setter
def full_loc(self, value: int):
self._full_loc = value
[docs]class FolderAnalysis(Analysis):
[docs] def __init__(self, folder: "Folder"):
self.lines = {key: 0 for key in ["code", "documentation", "empty", "string"]}
super().__init__(folder)
def __repr__(self):
return f"<FolderAnalysis(lines={self.lines})>"
[docs] def add_module_analysis(self, analysis: "ModuleAnalysis"):
for attr in self.lines:
value = getattr(analysis.source_analysis, attr)
if value is None:
value = 0
self.lines[attr] += value
[docs] def add_subfolder_analysis(self, analysis: "FolderAnalysis"):
for attr in self.lines:
value = analysis.lines[attr]
if value is None:
value = 0
self.lines[attr] += value
@property
def loc(self) -> int:
return self.lines["code"]
@loc.setter
def loc(self, value: int):
raise NotImplementedError
@property
def full_loc(self) -> int:
loc = 0
keys = ["code", "documentation", "empty", "string"]
for key in keys:
loc += self.lines[key]
return loc
@full_loc.setter
def full_loc(self, value: int):
raise NotImplementedError
[docs]class ModuleAnalysis(Analysis):
[docs] def __init__(self, module: "Module"):
self.module = module
self.source_analysis: pygount.SourceAnalysis = pygount.SourceAnalysis.from_file(
self.module.path, self.module.package
)
self.loc = self.source_analysis.code
full_loc = 0
for key in ["code", "documentation", "empty", "string"]:
full_loc += getattr(self.source_analysis, key, 0)
self.full_loc = full_loc
super().__init__(module)
[docs]class GitAnalysis:
[docs] def __init__(self, analyzable: "Analyzable"):
self.ref = analyzable
@cached_property
def commits(self) -> Optional[List[Commit]]:
if not self.has_repo:
return None
if self.ref.project is None: # for mypy
return None
commits = [
commit for commit in self.ref.project.repo.iter_commits(paths=self.ref.path)
]
return commits
@cached_property
def num_commits(self) -> Optional[int]:
if not self.has_repo:
return None
return len(self.commits)
@cached_property
def created(self) -> Optional[datetime]:
if not self.commits:
return None
return self.commits[-1].committed_datetime
@cached_property
def updated(self) -> Optional[datetime]:
if not self.commits:
return None
return self.commits[0].committed_datetime
@cached_property
def has_repo(self) -> bool:
return self.ref.project is not None and self.ref.project.repo is not None
@cached_property
def has_remote(self) -> bool:
if self.ref.project is None:
return False
try:
self.ref.project.repo.remote()
return True
except ValueError as e:
if "Remote named 'origin' didn't exist" in str(e):
# Repo does not have remote
return False
else:
raise e
@cached_property
def urls(self) -> Optional[List[str]]:
if not self.has_repo or not self.has_remote:
return None
if self.ref.project is None: # for mypy
return None
urls = list(self.ref.project.repo.remote().urls)
# Urls currently have .git on the end, strip
urls = [url[:-4] if url.endswith(".git") else url for url in urls]
return urls