acecalisto3 commited on
Commit
6c97e89
·
1 Parent(s): 6f46d77
Files changed (1) hide show
  1. app.py +196 -0
app.py CHANGED
@@ -839,6 +839,201 @@ if __name__ == "__main__":
839
  "previous": recent_values[0]
840
  }
841
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
842
  @dataclass
843
  class QualityMetrics:
844
  """Advanced quality metrics tracking and analysis"""
@@ -1025,6 +1220,7 @@ if __name__ == "__main__":
1025
  }
1026
 
1027
  class ProjectAnalytics:
 
1028
  """Enhanced project analytics and reporting"""
1029
  def __init__(self, workspace_manager):
1030
  self.workspace_manager = workspace_manager
 
839
  "previous": recent_values[0]
840
  }
841
 
842
+ class CodeMetricsAnalyzer:
843
+ """Analyzes code metrics using various tools"""
844
+
845
+ def __init__(self):
846
+ self.metrics_history = []
847
+
848
+ def analyze_code_quality(self, file_path: str) -> Dict[str, Any]:
849
+ """Analyzes code quality using multiple metrics"""
850
+ try:
851
+ # Pylint analysis
852
+ pylint_score = self._run_pylint(file_path)
853
+
854
+ # Complexity analysis
855
+ complexity_score = self._analyze_complexity(file_path)
856
+
857
+ # Test coverage analysis
858
+ coverage_score = self._analyze_test_coverage(file_path)
859
+
860
+ # Security analysis
861
+ security_score = self._analyze_security(file_path)
862
+
863
+ # Calculate overall quality score
864
+ quality_score = self._calculate_overall_score(
865
+ pylint_score,
866
+ complexity_score,
867
+ coverage_score,
868
+ security_score
869
+ )
870
+
871
+ metrics = {
872
+ "quality_score": quality_score,
873
+ "pylint_score": pylint_score,
874
+ "complexity_score": complexity_score,
875
+ "coverage_score": coverage_score,
876
+ "security_score": security_score,
877
+ "timestamp": datetime.now()
878
+ }
879
+
880
+ self.metrics_history.append(metrics)
881
+ return metrics
882
+
883
+ except Exception as e:
884
+ logging.error(f"Error analyzing code metrics: {str(e)}")
885
+ return {
886
+ "error": str(e),
887
+ "quality_score": 0.0,
888
+ "timestamp": datetime.now()
889
+ }
890
+
891
+ def _run_pylint(self, file_path: str) -> float:
892
+ """Runs pylint analysis"""
893
+ try:
894
+ reporter = JSONReporter()
895
+ Run([file_path], reporter=reporter, do_exit=False)
896
+ score = reporter.data.get('score', 0.0)
897
+ return float(score) / 10.0 # Normalize to 0-1 scale
898
+ except Exception as e:
899
+ logging.error(f"Pylint analysis error: {str(e)}")
900
+ return 0.0
901
+
902
+ def _analyze_complexity(self, file_path: str) -> float:
903
+ """Analyzes code complexity"""
904
+ try:
905
+ with open(file_path, 'r') as file:
906
+ code = file.read()
907
+
908
+ # Calculate cyclomatic complexity
909
+ complexity = radon.complexity.cc_visit(code)
910
+ avg_complexity = sum(item.complexity for item in complexity) / len(complexity) if complexity else 0
911
+
912
+ # Normalize complexity score (0-1 scale, lower is better)
913
+ normalized_score = 1.0 - min(avg_complexity / 10.0, 1.0)
914
+ return normalized_score
915
+
916
+ except Exception as e:
917
+ logging.error(f"Complexity analysis error: {str(e)}")
918
+ return 0.0
919
+
920
+ async def _analyze_current_state(self, project_name: str) -> Dict[str, Any]:
921
+ """Analyze current project state with detailed metrics."""
922
+ try:
923
+ self.logger.info(f"Analyzing current state for project: {project_name}")
924
+
925
+ # Collect code metrics
926
+ code_metrics = await self._collect_code_metrics(project_name)
927
+ self.logger.info("Code metrics collected successfully.")
928
+
929
+ # Analyze test coverage
930
+ test_coverage = await self._analyze_test_coverage(project_name)
931
+ self.logger.info("Test coverage analysis completed.")
932
+
933
+ # Check security vulnerabilities
934
+ security_analysis = await self._analyze_security(project_name)
935
+ self.logger.info("Security analysis completed.")
936
+
937
+ # Measure performance metrics
938
+ performance_metrics = await self._measure_performance(project_name)
939
+ self.logger.info("Performance metrics measured.")
940
+
941
+ # Determine if requirements are met
942
+ meets_requirements = await self._check_requirements(
943
+ code_metrics,
944
+ test_coverage,
945
+ security_analysis,
946
+ performance_metrics
947
+ )
948
+ self.logger.info("Requirements check completed.")
949
+
950
+ return {
951
+ "code_metrics": code_metrics,
952
+ "test_coverage": test_coverage,
953
+ "security_analysis": security_analysis,
954
+ "performance_metrics": performance_metrics,
955
+ "meets_requirements": meets_requirements,
956
+ "timestamp": datetime.now()
957
+ }
958
+
959
+ except Exception as e:
960
+ self.logger.error(f"Error analyzing current state: {str(e)}")
961
+ raise
962
+
963
+ def _analyze_security(self, file_path: str) -> float:
964
+ """Analyzes code security using bandit"""
965
+ try:
966
+ conf = manager.BanditManager()
967
+ conf.discover_files([file_path])
968
+ conf.run_tests()
969
+
970
+ # Calculate security score based on findings
971
+ total_issues = len(conf.get_issue_list())
972
+ max_severity = max((issue.severity for issue in conf.get_issue_list()), default=0)
973
+
974
+ # Normalize security score (0-1 scale, higher is better)
975
+ security_score = 1.0 - (total_issues * max_severity) / 10.0
976
+ return max(0.0, min(1.0, security_score))
977
+
978
+ except Exception as e:
979
+ logging.error(f"Security analysis error: {str(e)}")
980
+ return 0.0
981
+
982
+ def _calculate_overall_score(self, pylint_score: float, complexity_score: float,
983
+ coverage_score: float, security_score: float) -> float:
984
+ """Calculates overall code quality score"""
985
+ weights = {
986
+ 'pylint': 0.3,
987
+ 'complexity': 0.2,
988
+ 'coverage': 0.25,
989
+ 'security': 0.25
990
+ }
991
+
992
+ overall_score = (
993
+ weights['pylint'] * pylint_score +
994
+ weights['complexity'] * complexity_score +
995
+ weights['coverage'] * coverage_score +
996
+ weights['security'] * security_score
997
+ )
998
+
999
+ return max(0.0, min(1.0, overall_score))
1000
+
1001
+ def get_metrics_history(self) -> List[Dict[str, Any]]:
1002
+ """Returns the history of metrics measurements"""
1003
+ return self.metrics_history
1004
+
1005
+ def get_trend_analysis(self) -> Dict[str, Any]:
1006
+ """Analyzes trends in metrics over time"""
1007
+ if not self.metrics_history:
1008
+ return {"status": "No metrics history available"}
1009
+
1010
+ trends = {
1011
+ "quality_score": self._calculate_trend([m["quality_score"] for m in self.metrics_history]),
1012
+ "coverage_score": self._calculate_trend([m["coverage_score"] for m in self.metrics_history]),
1013
+ "security_score": self._calculate_trend([m["security_score"] for m in self.metrics_history])
1014
+ }
1015
+
1016
+ return trends
1017
+
1018
+ def _calculate_trend(self, values: List[float]) -> Dict[str, Any]:
1019
+ """Calculates trend statistics for a metric"""
1020
+ if not values:
1021
+ return {"trend": "unknown", "change": 0.0}
1022
+
1023
+ recent_values = values[-3:] # Look at last 3 measurements
1024
+ if len(recent_values) < 2:
1025
+ return {"trend": "insufficient data", "change": 0.0}
1026
+
1027
+ change = recent_values[-1] - recent_values[0]
1028
+ trend = "improving" if change > 0 else "declining" if change < 0 else "stable"
1029
+
1030
+ return {
1031
+ "trend": trend,
1032
+ "change": change,
1033
+ "current": recent_values[-1],
1034
+ "previous": recent_values[0]
1035
+ }
1036
+
1037
  @dataclass
1038
  class QualityMetrics:
1039
  """Advanced quality metrics tracking and analysis"""
 
1220
  }
1221
 
1222
  class ProjectAnalytics:
1223
+ """Enhanced project analytics and reporting"""
1224
  """Enhanced project analytics and reporting"""
1225
  def __init__(self, workspace_manager):
1226
  self.workspace_manager = workspace_manager