This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from dataclasses_json import dataclass_json
|
from dataclasses_json import dataclass_json
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ from dataclasses_json import dataclass_json
|
|||||||
@dataclass_json
|
@dataclass_json
|
||||||
@dataclass
|
@dataclass
|
||||||
class Author:
|
class Author:
|
||||||
"""Represents a git author."""
|
"""Author information."""
|
||||||
name: str
|
name: str
|
||||||
email: str
|
email: str
|
||||||
commit_count: int = 0
|
commit_count: int = 0
|
||||||
@@ -19,77 +19,134 @@ class Author:
|
|||||||
@dataclass_json
|
@dataclass_json
|
||||||
@dataclass
|
@dataclass
|
||||||
class Commit:
|
class Commit:
|
||||||
"""Represents a git commit."""
|
"""Git commit information."""
|
||||||
sha: str
|
sha: str
|
||||||
message: str
|
message: str
|
||||||
author: str
|
author_name: str
|
||||||
author_email: str
|
author_email: str
|
||||||
timestamp: datetime
|
committed_datetime: datetime
|
||||||
lines_added: int = 0
|
author_datetime: datetime
|
||||||
lines_deleted: int = 0
|
additions: int = 0
|
||||||
|
deletions: int = 0
|
||||||
files_changed: List[str] = field(default_factory=list)
|
files_changed: List[str] = field(default_factory=list)
|
||||||
|
parents: List[str] = field(default_factory=list)
|
||||||
is_merge: bool = False
|
is_merge: bool = False
|
||||||
is_revert: bool = False
|
is_revert: bool = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lines_changed_count(self) -> int:
|
||||||
|
"""Get total lines changed."""
|
||||||
|
return self.additions + self.deletions
|
||||||
|
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
@dataclass
|
@dataclass
|
||||||
class FileChange:
|
class FileChange:
|
||||||
"""Represents changes to a file in a commit."""
|
"""File change information."""
|
||||||
filepath: str
|
filepath: str
|
||||||
lines_added: int
|
lines_added: int = 0
|
||||||
lines_deleted: int
|
lines_deleted: int = 0
|
||||||
change_type: str
|
change_type: str = "M"
|
||||||
|
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
@dataclass
|
@dataclass
|
||||||
class CommitAnalysis:
|
class CommitAnalysis:
|
||||||
"""Analysis of commit patterns."""
|
"""Commit pattern analysis results."""
|
||||||
total_commits: int
|
total_commits: int = 0
|
||||||
unique_authors: int
|
unique_authors: int = 0
|
||||||
commits_by_hour: dict
|
commits_by_hour: Dict[str, int] = field(default_factory=dict)
|
||||||
commits_by_day: dict
|
commits_by_day: Dict[str, int] = field(default_factory=dict)
|
||||||
commits_by_week: dict
|
commits_by_week: Dict[str, int] = field(default_factory=dict)
|
||||||
top_authors: List[Author]
|
commits_by_month: Dict[str, int] = field(default_factory=dict)
|
||||||
average_commits_per_day: float
|
top_authors: List[Author] = field(default_factory=list)
|
||||||
|
average_commits_per_day: float = 0.0
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary."""
|
||||||
|
return {
|
||||||
|
"total_commits": self.total_commits,
|
||||||
|
"unique_authors": self.unique_authors,
|
||||||
|
"commits_by_hour": self.commits_by_hour,
|
||||||
|
"commits_by_day": self.commits_by_day,
|
||||||
|
"commits_by_week": self.commits_by_week,
|
||||||
|
"commits_by_month": self.commits_by_month,
|
||||||
|
"top_authors": [a.to_dict() for a in self.top_authors],
|
||||||
|
"average_commits_per_day": self.average_commits_per_day,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
@dataclass
|
@dataclass
|
||||||
class CodeChurnAnalysis:
|
class CodeChurnAnalysis:
|
||||||
"""Analysis of code churn."""
|
"""Code churn analysis results."""
|
||||||
total_lines_added: int
|
total_lines_added: int = 0
|
||||||
total_lines_deleted: int
|
total_lines_deleted: int = 0
|
||||||
net_change: int
|
net_change: int = 0
|
||||||
churn_by_file: dict
|
churn_by_file: Dict[str, int] = field(default_factory=dict)
|
||||||
churn_by_author: dict
|
churn_by_author: Dict[str, int] = field(default_factory=dict)
|
||||||
high_churn_commits: List[Commit]
|
high_churn_files: List[FileChange] = field(default_factory=list)
|
||||||
average_churn_per_commit: float
|
high_churn_commits: List[Commit] = field(default_factory=list)
|
||||||
|
average_churn_per_commit: float = 0.0
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary."""
|
||||||
|
return {
|
||||||
|
"total_lines_added": self.total_lines_added,
|
||||||
|
"total_lines_deleted": self.total_lines_deleted,
|
||||||
|
"net_change": self.net_change,
|
||||||
|
"churn_by_file": self.churn_by_file,
|
||||||
|
"churn_by_author": self.churn_by_author,
|
||||||
|
"high_churn_files": [f.to_dict() for f in self.high_churn_files],
|
||||||
|
"high_churn_commits": [c.to_dict() for c in self.high_churn_commits],
|
||||||
|
"average_churn_per_commit": self.average_churn_per_commit,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
@dataclass
|
@dataclass
|
||||||
class RiskyCommitAnalysis:
|
class RiskyCommitAnalysis:
|
||||||
"""Analysis of risky commits."""
|
"""Risky commit detection results."""
|
||||||
total_risky_commits: int
|
large_commits: List[Commit] = field(default_factory=list)
|
||||||
large_change_commits: List[Commit]
|
merge_commits: List[Commit] = field(default_factory=list)
|
||||||
merge_commits: List[Commit]
|
revert_commits: List[Commit] = field(default_factory=list)
|
||||||
revert_commits: List[Commit]
|
total_risky: int = 0
|
||||||
risk_score: float
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary."""
|
||||||
|
return {
|
||||||
|
"large_commits": [c.to_dict() for c in self.large_commits],
|
||||||
|
"merge_commits": [c.to_dict() for c in self.merge_commits],
|
||||||
|
"revert_commits": [c.to_dict() for c in self.revert_commits],
|
||||||
|
"total_risky": self.total_risky,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
@dataclass
|
@dataclass
|
||||||
class VelocityAnalysis:
|
class VelocityAnalysis:
|
||||||
"""Analysis of team velocity."""
|
"""Velocity analysis results."""
|
||||||
commits_per_day: float
|
total_commits: int = 0
|
||||||
commits_per_week: float
|
commits_per_day: float = 0.0
|
||||||
commits_per_month: float
|
commits_per_week: float = 0.0
|
||||||
velocity_trend: str
|
velocity_trend: str = "stable"
|
||||||
top_contributors: List[Author]
|
most_active_day: str = ""
|
||||||
most_active_day: str
|
most_active_hour: str = ""
|
||||||
most_active_hour: str
|
weekly_totals: Dict[str, int] = field(default_factory=dict)
|
||||||
|
daily_totals: Dict[str, int] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""Convert to dictionary."""
|
||||||
|
return {
|
||||||
|
"total_commits": self.total_commits,
|
||||||
|
"commits_per_day": self.commits_per_day,
|
||||||
|
"commits_per_week": self.commits_per_week,
|
||||||
|
"velocity_trend": self.velocity_trend,
|
||||||
|
"most_active_day": self.most_active_day,
|
||||||
|
"most_active_hour": self.most_active_hour,
|
||||||
|
"weekly_totals": self.weekly_totals,
|
||||||
|
"daily_totals": self.daily_totals,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
@@ -98,7 +155,8 @@ class ProductivityReport:
|
|||||||
"""Comprehensive productivity report."""
|
"""Comprehensive productivity report."""
|
||||||
repository_path: str
|
repository_path: str
|
||||||
analysis_days: int
|
analysis_days: int
|
||||||
commit_analysis: Optional[CommitAnalysis]
|
commit_analysis: Optional[CommitAnalysis] = None
|
||||||
code_churn_analysis: Optional[CodeChurnAnalysis]
|
code_churn_analysis: Optional[CodeChurnAnalysis] = None
|
||||||
risky_commit_analysis: Optional[RiskyCommitAnalysis]
|
risky_commit_analysis: Optional[RiskyCommitAnalysis] = None
|
||||||
velocity_analysis: Optional[VelocityAnalysis]
|
velocity_analysis: Optional[VelocityAnalysis] = None
|
||||||
|
generated_at: datetime = field(default_factory=datetime.now)
|
||||||
|
|||||||
Reference in New Issue
Block a user