Spaces:
Sleeping
Sleeping
Commit
·
6c97e89
1
Parent(s):
6f46d77
app fix
Browse files
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
|