Skip to content

Commit 699338d

Browse files
authored
Merge pull request #14 from n0rfas/dev
2 parents f7de4f6 + f6b8382 commit 699338d

27 files changed

+948
-309
lines changed

.github/workflows/code-check-dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ jobs:
4444
- run: pipx install "poetry~=2.0.0"
4545
- run: poetry install --with dev
4646
- run: poetry run ruff check --select I .
47-
- run: poetry run pytest --cov=git_analytics --cov-report=term-missing --cov-fail-under=40
47+
- run: poetry run pytest --cov=git_analytics --cov-report=term-missing --cov-fail-under=45

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ poetry run git-analytics
4949

5050
```bash
5151
poetry run pytest
52-
poetry run pytest --cov=git_analytics --cov-report=term-missing --cov-fail-under=40
52+
poetry run pytest --cov=git_analytics --cov-report=term-missing --cov-fail-under=45
5353
```
5454

5555
### Type Checking

git_analytics/analyzers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
from .commit_type import CommitTypeAnalyzer
33
from .commits_summary import CommitsSummaryAnalyzer
44
from .historical_statistics import HistoricalStatisticsAnalyzer
5+
from .language_statistics import LanguageAnalyzer
56
from .lines_statistics import LinesAnalyzer
67

78
__all__ = [
89
"AuthorsStatisticsAnalyzer",
910
"CommitTypeAnalyzer",
1011
"CommitsSummaryAnalyzer",
1112
"HistoricalStatisticsAnalyzer",
13+
"LanguageAnalyzer",
1214
"LinesAnalyzer",
1315
]
Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,57 @@
11
from collections import Counter, defaultdict
22
from dataclasses import dataclass
3-
from datetime import date
4-
from enum import Enum
5-
from typing import Dict
3+
from typing import Dict, List
64

75
from git_analytics.entities import AnalyticsCommit, AnalyticsResult
6+
from git_analytics.helpers import get_number_week
87
from git_analytics.interfaces import CommitAnalyzer
98

109

11-
class CommitType(Enum):
12-
FEATURE = "feature"
13-
FIX = "fix"
14-
DOCS = "docs"
15-
STYLE = "style"
16-
REFACTOR = "refactor"
17-
TEST = "test"
18-
CHORE = "chore"
19-
WIP = "wip"
20-
MERGE = "merge"
21-
UNKNOWN = "unknown"
22-
23-
2410
@dataclass
2511
class Result(AnalyticsResult):
26-
items: Dict[date, Dict[CommitType, int]]
12+
commit_type_by_week: Dict[str, Dict[str, int]]
13+
commit_type_counter: Dict[str, int]
14+
author_commit_type_by_week: Dict[str, Dict[str, Dict[str, int]]]
15+
author_commit_type_counter: Dict[str, Dict[str, int]]
2716

2817

29-
TYPE_COMMIT_LIST: tuple = tuple(ct.value for ct in CommitType)
18+
LIST_OF_TYPE_COMMIT: List[str] = ["feature", "fix", "docs", "style", "refactor", "test", "chore", "wip", "merge"]
3019

3120

32-
def _get_type_list(commit_message: str):
33-
result = [tag for tag in TYPE_COMMIT_LIST if tag in commit_message]
21+
def _get_type_list(commit_message: str) -> List[str]:
22+
result = [tag for tag in LIST_OF_TYPE_COMMIT if tag in commit_message.lower()]
3423
if result:
3524
return result
36-
return [CommitType.UNKNOWN]
25+
return ["unknown"]
3726

3827

3928
class CommitTypeAnalyzer(CommitAnalyzer):
4029
name = "commit_type"
4130

4231
def __init__(self) -> None:
43-
self._by_date: Dict[date, Counter] = defaultdict(Counter)
32+
self._commit_type_by_week: Dict[str, Counter] = defaultdict(Counter)
33+
self._commit_type_counter: Counter = Counter()
34+
self._author_commit_type_by_week: Dict[str, Dict[str, Counter]] = defaultdict(lambda: defaultdict(Counter))
35+
self._author_commit_type_counter: Dict[str, Counter] = defaultdict(Counter)
4436

4537
def process(self, commit: AnalyticsCommit) -> None:
46-
commit_date = commit.committed_datetime.date()
38+
week_number = get_number_week(commit.committed_datetime)
4739
commit_types = _get_type_list(commit.message)
4840
for commit_type in commit_types:
49-
self._by_date[commit_date][commit_type] += 1
41+
self._commit_type_by_week[week_number][commit_type] += 1
42+
self._commit_type_counter[commit_type] += 1
43+
self._author_commit_type_by_week[commit.commit_author][week_number][commit_type] += 1
44+
self._author_commit_type_counter[commit.commit_author][commit_type] += 1
5045

5146
def result(self) -> Result:
52-
return Result(items={dt: dict(counter) for dt, counter in self._by_date.items()})
47+
return Result(
48+
commit_type_by_week={wn: dict(sorted(c.items())) for wn, c in sorted(self._commit_type_by_week.items())},
49+
commit_type_counter=dict(sorted(self._commit_type_counter.items())),
50+
author_commit_type_by_week={
51+
a: {wn: dict(sorted(c.items())) for wn, c in sorted(weeks.items())}
52+
for a, weeks in sorted(self._author_commit_type_by_week.items())
53+
},
54+
author_commit_type_counter={
55+
a: dict(sorted(c.items())) for a, c in sorted(self._author_commit_type_counter.items())
56+
},
57+
)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from collections import defaultdict
2+
from dataclasses import dataclass
3+
from typing import Dict
4+
5+
from git_analytics.entities import AnalyticsCommit, AnalyticsResult
6+
from git_analytics.interfaces import CommitAnalyzer
7+
8+
9+
@dataclass
10+
class FileExtensionChangeStats:
11+
insertions: int = 0
12+
deletions: int = 0
13+
14+
15+
@dataclass
16+
class Result(AnalyticsResult):
17+
files_extensions_total: Dict[str, FileExtensionChangeStats]
18+
files_extensions_by_author: Dict[str, Dict[str, FileExtensionChangeStats]]
19+
20+
21+
def _get_file_extension(file_path: str) -> str:
22+
filename = file_path.split("/")[-1]
23+
filename_parts = filename.split(".")
24+
if len(filename_parts) == 1 or filename_parts[0] == "":
25+
return "no_extension"
26+
return filename_parts[-1].lower().replace("}", "")
27+
28+
29+
class LanguageAnalyzer(CommitAnalyzer):
30+
name = "language_statistics"
31+
32+
def __init__(self) -> None:
33+
self._total: Dict[str, FileExtensionChangeStats] = defaultdict(FileExtensionChangeStats)
34+
self._by_author: Dict[str, Dict[str, FileExtensionChangeStats]] = defaultdict(
35+
lambda: defaultdict(FileExtensionChangeStats)
36+
)
37+
38+
def process(self, commit: AnalyticsCommit) -> None:
39+
for changed_file in commit.files:
40+
file_extension = _get_file_extension(changed_file)
41+
self._total[file_extension].insertions += commit.files[changed_file].insertions
42+
self._total[file_extension].deletions += commit.files[changed_file].deletions
43+
self._by_author[commit.commit_author][file_extension].insertions += commit.files[changed_file].insertions
44+
self._by_author[commit.commit_author][file_extension].deletions += commit.files[changed_file].deletions
45+
46+
def result(self) -> Result:
47+
return Result(
48+
files_extensions_total=dict(sorted(self._total.items(), key=lambda item: item[1].insertions, reverse=True)),
49+
files_extensions_by_author={
50+
author: dict(sorted(counter.items(), key=lambda item: item[1].insertions, reverse=True))
51+
for author, counter in self._by_author.items()
52+
},
53+
)

git_analytics/engine.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from datetime import date, timezone
2-
from typing import Dict, Optional
2+
from typing import Any, Dict, Optional
33

44
from git_analytics.entities import AnalyticsResult
55
from git_analytics.interfaces import CommitSource
@@ -10,9 +10,11 @@ def __init__(
1010
self,
1111
source: CommitSource,
1212
analyzers_factory,
13+
additional_data: Optional[Dict[str, Any]] = None,
1314
) -> None:
1415
self._source = source
1516
self._analyzers_factory = analyzers_factory
17+
self._additional_data = additional_data
1618

1719
def run(
1820
self,
@@ -36,4 +38,7 @@ def run(
3638
for analyzer in analyzers:
3739
analyzer.process(commit)
3840

39-
return {analyzer.name: analyzer.result() for analyzer in analyzers}
41+
result = {analyzer.name: analyzer.result() for analyzer in analyzers}
42+
if self._additional_data:
43+
result["additional_data"] = self._additional_data
44+
return result

git_analytics/entities.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
from typing import Any, Dict
66

77

8+
@dataclass
9+
class FileChangeStats:
10+
insertions: int
11+
deletions: int
12+
13+
814
@dataclass
915
class AnalyticsCommit:
1016
sha: str
@@ -14,11 +20,15 @@ class AnalyticsCommit:
1420
lines_deletions: int
1521
files_changed: int
1622
message: str
23+
files: Dict[str, FileChangeStats]
1724

1825

1926
@dataclass
2027
class AnalyticsResult:
21-
def to_dict(self) -> Dict[str, Any]:
28+
def __iter__(self):
29+
return iter(self._to_dict().items())
30+
31+
def _to_dict(self) -> Dict[str, Any]:
2232
return self._make_json_safe(asdict(self))
2333

2434
@staticmethod

git_analytics/helpers.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from datetime import date, datetime
2+
from typing import Union
3+
4+
5+
def get_number_week(dt: Union[date, datetime]) -> str:
6+
year = dt.year
7+
week = dt.isocalendar()[1]
8+
return f"{year}-W{week:02d}" # ISO-8601 week number

git_analytics/main.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from wsgiref.simple_server import make_server
23

34
from git import InvalidGitRepositoryError, Repo
@@ -7,6 +8,7 @@
78
CommitsSummaryAnalyzer,
89
CommitTypeAnalyzer,
910
HistoricalStatisticsAnalyzer,
11+
LanguageAnalyzer,
1012
LinesAnalyzer,
1113
)
1214
from git_analytics.engine import CommitAnalyticsEngine
@@ -20,20 +22,24 @@ def make_analyzers():
2022
CommitsSummaryAnalyzer(),
2123
CommitTypeAnalyzer(),
2224
HistoricalStatisticsAnalyzer(),
25+
LanguageAnalyzer(),
2326
LinesAnalyzer(),
2427
]
2528

2629

2730
def run():
2831
try:
29-
repo = Repo()
32+
path_repo = os.getenv("PATH_REPO", ".")
33+
repo = Repo(path_repo)
34+
name_branch = repo.active_branch.name
3035
except InvalidGitRepositoryError:
3136
print("Error: Current directory is not a git repository.")
3237
return
3338

3439
engine = CommitAnalyticsEngine(
3540
source=GitCommitSource(repo),
3641
analyzers_factory=make_analyzers,
42+
additional_data={"name_branch": name_branch},
3743
)
3844

3945
web_app = create_web_app(engine=engine)

git_analytics/sources/git_commit_adapter.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from git import Repo
44
from git.objects import Commit
55

6-
from git_analytics.entities import AnalyticsCommit
6+
from git_analytics.entities import AnalyticsCommit, FileChangeStats
77
from git_analytics.interfaces import CommitSource
88

99

@@ -25,4 +25,8 @@ def git_commit_to_analytics_commit(commit: Commit) -> AnalyticsCommit:
2525
lines_deletions=commit.stats.total["deletions"],
2626
files_changed=commit.stats.total["files"],
2727
message=str(commit.summary).strip(),
28+
files={
29+
str(file): FileChangeStats(insertions=value.get("insertions", 0), deletions=value.get("deletions", 0))
30+
for file, value in commit.stats.files.items()
31+
},
2832
)

0 commit comments

Comments
 (0)