1+ from ...config .data import (
2+ commit_types ,
3+ example_commits ,
4+ commit_training_data
5+ )
6+ from fastapi import HTTPException , status
7+ from ..models import CommitIssue
8+ from .format_analyzer import FormatAnalyzer
9+ from .quality_analyzer import QualityAnalyzer
10+ from datetime import datetime
11+ import ast
12+
13+
14+ class CommitAnalyzer :
15+ """
16+ Analyzes Git commit messages using a combination of pattern matching,
17+ machine learning, and semantic analysis to ensure commit quality and
18+ provide improvement suggestions.
19+ """
20+ def __init__ (self , settings : list ) -> None :
21+ """Initializes the analyzer with custom settings and prepares the ML classifier."""
22+ self .settings = settings
23+ self .slack_url = None # Retrieved from settings
24+ self .commit_types = commit_types
25+ self .example_commits = example_commits .copy ()
26+ self .commit_training_data = commit_training_data .copy ()
27+
28+ try :
29+ self ._apply_data_settings ()
30+ except Exception as e :
31+ raise HTTPException (
32+ status_code = status .HTTP_400_BAD_REQUEST ,
33+ detail = f"Invalid settings data: { str (e )} " ,
34+ )
35+
36+ def _apply_data_settings (self ) -> None :
37+ """
38+ Updates analyzer configuration with custom settings provided through Telex.
39+ Custom settings can override default commit types, examples, and training data.
40+ Provides slack webhook url.
41+ """
42+ for setting in self .settings :
43+ if setting ["label" ] == "commit_types" :
44+ self .commit_types .update (ast .literal_eval (setting ["default" ].replace ("\n " , "\\ n" ))) if setting ["default" ] else self .commit_types
45+ if setting ["label" ] == "example_commits" :
46+ self .example_commits .update (ast .literal_eval (setting ["default" ].replace ("\n " , "\\ n" ))) if setting ["default" ] else self .example_commits
47+ if setting ["label" ] == "training_data" :
48+ self .commit_training_data .update (ast .literal_eval (setting ["default" ].replace ("\n " , "\\ n" ))) if setting ["default" ] else self .commit_training_data
49+ if setting ["label" ] == "slack_url" :
50+ self .slack_url = setting ["default" ]
51+
52+ def _check_content_format (self , message : str ) -> list [CommitIssue ]:
53+ format_analyzer = FormatAnalyzer (message , self .commit_types , self .example_commits )
54+ return format_analyzer .check_all ()
55+
56+ def _check_content_quality (self , message : str ) -> list [CommitIssue ]:
57+ quality_analyzer = QualityAnalyzer (message )
58+ return quality_analyzer .check_all ()
59+
60+ def analyze_commit (self , message : str ) -> list [CommitIssue ]:
61+ """Analyzes a commit message and returns any quality issues found."""
62+ issues = []
63+ issues .extend ([* self ._check_content_format (message )])
64+ issues .extend ([* self ._check_content_quality (message )])
65+ return [issue for issue in issues if issue ]
66+
67+ def format_analysis (self , commit : dict , issues : list [CommitIssue ]) -> str :
68+ """Formats analysis results into a human-readable message for Slack."""
69+ icons = {"high" : "🔴" , "medium" : "🟡" , "low" : "🔵" }
70+
71+ timestamp = datetime .fromisoformat (commit ["timestamp" ].replace ("Z" , "+00:00" ))
72+ formatted_time = timestamp .strftime ("%-I:%M%p. %A, %B %-d, %Y." )
73+
74+ author = commit ["author" ]
75+ author_info = f"{ author ['name' ]} ({ author .get ('email' )} )"
76+
77+ commit_details = (
78+ "📝 *Commit Details*\n "
79+ f"└─ Hash: `{ commit ['id' ][:8 ]} `\n "
80+ f"└─ Author: { author_info } \n "
81+ f"└─ URL: <{ commit ['url' ]} |commit url>\n "
82+ f"└─ Time: { formatted_time } \n "
83+ f"└─ Message:\n "
84+ f"• ```{ commit ['message' ]} ```\n "
85+ )
86+
87+ if issues :
88+ issues_text = "\n " .join (
89+ f"{ icons [issue .severity ]} { issue .message } \n "
90+ f" └─ { issue .suggestion .replace (chr (10 ), chr (10 ) + ' ' )} "
91+ for issue in sorted (issues , key = lambda x : x .severity )
92+ )
93+ analysis_section = "\n 🔍 *Analysis Results*\n " f"{ issues_text } \n "
94+
95+ suggestions = (
96+ "\n 💡 Resources\n "
97+ "└─ Conventional Commits: <https://www.conventionalcommits.org|Conventional Commits>\n "
98+ "└─ Commit Best Practices: <https://dev.to/sheraz4194/good-commit-vs-bad-commit-best-practices-for-git-1plc|Best Practices>\n "
99+ "└─ Git Best Practices: <https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project|Git Contributing>"
100+ )
101+
102+ return f"{ commit_details } { analysis_section } { suggestions } "
0 commit comments