forked from floraachy/Photoshop
611 lines
22 KiB
Python
611 lines
22 KiB
Python
import configparser
|
|
import math
|
|
import requests
|
|
import json
|
|
import os
|
|
|
|
from django.http import JsonResponse
|
|
from django.views.decorators.http import require_http_methods
|
|
|
|
from maturityAnalyse import settings
|
|
|
|
config_file_path = os.path.join(settings.BASE_DIR, 'static', 'config.ini')
|
|
|
|
|
|
# 加载配置文件
|
|
def load_config(file_path):
|
|
config = configparser.ConfigParser()
|
|
config.read(file_path)
|
|
return config['Limits']
|
|
|
|
|
|
# 获取最大最小值
|
|
def get_extreme_values(name):
|
|
limits = load_config(config_file_path)
|
|
|
|
try:
|
|
max_value = int(limits[f"{name}_max"])
|
|
min_value = int(limits[f"{name}_min"])
|
|
except KeyError:
|
|
print(f"没有找到配置项: {name}")
|
|
max_value, min_value = None, None
|
|
|
|
return max_value, min_value
|
|
|
|
|
|
# 获取成熟度计算所需的上下文(最大最小值)
|
|
def get_maturity_context():
|
|
context = MaturityContext()
|
|
context.commits_number_max, context.commits_number_min = get_extreme_values("commits_number")
|
|
context.contributing_max, context.contributing_min = get_extreme_values("contributing")
|
|
context.critical_advisory_max, context.critical_advisory_min = get_extreme_values("critical_advisory")
|
|
context.forks_max, context.forks_min = get_extreme_values("forks")
|
|
context.high_advisory_max, context.high_advisory_min = get_extreme_values("high_advisory")
|
|
context.issue_closed_max, context.issue_closed_min = get_extreme_values("issue_closed")
|
|
context.issue_opened_max, context.issue_opened_min = get_extreme_values("issue_opened")
|
|
context.license_max, context.license_min = get_extreme_values("license_info")
|
|
context.low_advisory_max, context.low_advisory_min = get_extreme_values("low_advisory")
|
|
context.moderate_advisory_max, context.moderate_advisory_min = get_extreme_values("moderate_advisory")
|
|
context.stars_max, context.stars_min = get_extreme_values("stars")
|
|
context.release_number_max, context.release_number_min = get_extreme_values("release_number")
|
|
context.developer_number_max, context.developer_number_min = get_extreme_values("developer_number")
|
|
|
|
return context
|
|
|
|
|
|
class MaturityContext:
|
|
def __init__(self):
|
|
self.stars_max = 0
|
|
self.stars_min = 0
|
|
self.watching_max = 0
|
|
self.watching_min = 0
|
|
self.forks_max = 0
|
|
self.forks_min = 0
|
|
self.commits_number_max = 0
|
|
self.commits_number_min = 0
|
|
self.license_max = 0
|
|
self.license_min = 0
|
|
self.issue_closed_max = 0
|
|
self.issue_closed_min = 0
|
|
self.issue_opened_max = 0
|
|
self.issue_opened_min = 0
|
|
self.low_advisory_max = 0
|
|
self.low_advisory_min = 0
|
|
self.moderate_advisory_max = 0
|
|
self.moderate_advisory_min = 0
|
|
self.high_advisory_max = 0
|
|
self.high_advisory_min = 0
|
|
self.critical_advisory_max = 0
|
|
self.critical_advisory_min = 0
|
|
self.contributing_max = 0
|
|
self.contributing_min = 0
|
|
self.developer_number_max = 0
|
|
self.developer_number_min = 0
|
|
self.release_number_max = 0
|
|
self.release_number_min = 0
|
|
|
|
|
|
class Software:
|
|
def __init__(self, license, issue_opened, issue_closed, low_advisory, moderate_advisory, high_advisory,
|
|
critical_advisory, stars, watching, forks, commits_number, average_day_interval, readme,
|
|
contributing, developer_number, release_number, user_number, sum_user_score):
|
|
self.license = license
|
|
self.issue_opened = issue_opened
|
|
self.issue_closed = issue_closed
|
|
self.low_advisory = low_advisory
|
|
self.moderate_advisory = moderate_advisory
|
|
self.high_advisory = high_advisory
|
|
self.critical_advisory = critical_advisory
|
|
self.stars = stars
|
|
self.watching = watching
|
|
self.forks = forks
|
|
self.commits_number = commits_number
|
|
self.contributing = contributing
|
|
self.developer_number = developer_number
|
|
self.release_number = release_number
|
|
|
|
|
|
class AllWeightSoftware:
|
|
def __init__(self, software):
|
|
self.s = software
|
|
self.m = 0
|
|
|
|
|
|
def generate_score(value, max_value, min_value):
|
|
if max_value - min_value == 0:
|
|
return 100 if value > 0 else 0
|
|
|
|
if max_value > 2:
|
|
return log_generate_score(value, max_value, min_value)
|
|
else:
|
|
return normal_generate_score(value, max_value, min_value)
|
|
|
|
|
|
def normal_generate_score(value, max_value, min_value):
|
|
return int(100 * (value - min_value) / (max_value - min_value))
|
|
|
|
|
|
def log_generate_score(value, max_value, min_value):
|
|
if max_value - min_value == 0 and max_value != 0:
|
|
return 50
|
|
|
|
fv = math.log10(value) if value > 0 else 0
|
|
fmax = math.log10(max_value) if max_value != 0 else 0
|
|
fmin = math.log10(min_value) if min_value != 0 else 0
|
|
|
|
return int(100 * (fv - fmin) / (fmax - fmin))
|
|
|
|
|
|
def maturity(software, context):
|
|
ws = AllWeightSoftware(software)
|
|
|
|
# 计算每个属性的分数
|
|
ws.license_score = generate_score(software.license, context.license_max, context.license_min)
|
|
|
|
ws.open_issue_score = generate_score(software.issue_opened, context.issue_opened_max,
|
|
context.issue_opened_min)
|
|
|
|
ws.closed_issue_score = generate_score(software.issue_closed, context.issue_closed_max, context.issue_closed_min)
|
|
|
|
ws.low_advisory_score = 100 - generate_score(software.low_advisory, context.low_advisory_max,
|
|
context.low_advisory_min)
|
|
|
|
ws.moderate_advisory_score = 100 - generate_score(software.moderate_advisory, context.moderate_advisory_max,
|
|
context.moderate_advisory_min)
|
|
|
|
ws.high_advisory_score = 100 - generate_score(software.high_advisory, context.high_advisory_max,
|
|
context.high_advisory_min)
|
|
|
|
ws.critical_advisory_score = 100 - generate_score(software.critical_advisory, context.critical_advisory_max,
|
|
context.critical_advisory_min)
|
|
|
|
ws.stars_score = generate_score(software.stars, context.stars_max, context.stars_min)
|
|
|
|
ws.watching_score = generate_score(software.watching, context.watching_max, context.watching_min)
|
|
|
|
ws.fork_score = generate_score(software.forks, context.forks_max, context.forks_min)
|
|
|
|
ws.commits_number_score = generate_score(software.commits_number, context.commits_number_max,
|
|
context.commits_number_min)
|
|
|
|
ws.developer_number_score = generate_score(software.developer_number, context.developer_number_max,
|
|
context.developer_number_min)
|
|
|
|
ws.release_number_score = generate_score(software.release_number, context.release_number_max,
|
|
context.release_number_min)
|
|
|
|
# 计算整体成熟度,去掉权重影响
|
|
ws.m = (ws.license_score + ws.open_issue_score + ws.closed_issue_score +
|
|
# 降低了这部分内容的权重
|
|
(ws.low_advisory_score + ws.moderate_advisory_score + ws.high_advisory_score + ws.critical_advisory_score) * 0.25 +
|
|
ws.stars_score + ws.watching_score + ws.fork_score + ws.commits_number_score +
|
|
ws.developer_number_score + ws.release_number_score) / 10
|
|
|
|
stage = 0
|
|
if ws.m < 60:
|
|
stage = 2
|
|
elif ws.m >= 60 and ws.m < 80:
|
|
stage = 1
|
|
|
|
yadd = stage * 3 - 1
|
|
|
|
scores_charts = [
|
|
{
|
|
"name": "license_score",
|
|
"score": ws.license_score,
|
|
"description": "开源许可证评分",
|
|
"criteria": {
|
|
"100": "使用标准开源许可证(MIT/Apache/GPL等)",
|
|
"0": "无许可证"
|
|
},
|
|
"suggestion": "建议使用标准的开源许可证以明确项目的使用条款和限制",
|
|
"itemStyle": {
|
|
"color": "#e9ff40"
|
|
},
|
|
"value": [
|
|
3,
|
|
1 + yadd,
|
|
ws.license_score
|
|
]
|
|
},
|
|
{
|
|
"name": "open_issue_score",
|
|
"score": ws.open_issue_score,
|
|
"description": "未解决issue评分",
|
|
"criteria": {
|
|
"80-100": "未解决issue较少",
|
|
"60-80": "未解决issue较多",
|
|
"0-60": "未解决issue很多"
|
|
},
|
|
"suggestion": "保持适度的未解决issue数量,及时处理重要issue",
|
|
"itemStyle": {
|
|
"color": "#abd9e9"
|
|
},
|
|
"value": [
|
|
7,
|
|
2 + yadd,
|
|
ws.open_issue_score
|
|
]
|
|
},
|
|
{
|
|
"name": "closed_issue_score",
|
|
"score": ws.closed_issue_score,
|
|
"description": "已解决issue评分",
|
|
"criteria": {
|
|
"80-100": "已解决issue很多",
|
|
"60-80": "已解决issue较多",
|
|
"0-60": "已解决issue较少"
|
|
},
|
|
"suggestion": "提高issue解决率,保持良好的问题跟踪和处理机制",
|
|
"itemStyle": {
|
|
"color": "#74add1"
|
|
},
|
|
"value": [
|
|
8,
|
|
2 + yadd,
|
|
ws.closed_issue_score
|
|
]
|
|
},
|
|
{
|
|
"name": "low_advisory_score",
|
|
"score": ws.low_advisory_score,
|
|
"description": "低风险安全警告评分",
|
|
"criteria": {
|
|
"100": "无低风险警告",
|
|
"0": "多个低风险警告"
|
|
},
|
|
"suggestion": "定期检查并修复安全隐患,即使是低风险问题也应重视",
|
|
"itemStyle": {
|
|
"color": "#ffffbf"
|
|
},
|
|
"value": [
|
|
0,
|
|
1 + yadd,
|
|
ws.low_advisory_score
|
|
]
|
|
},
|
|
{
|
|
"name": "moderate_advisory_score",
|
|
"score": ws.moderate_advisory_score,
|
|
"description": "中等风险安全警告评分",
|
|
"criteria": {
|
|
"100": "无中等风险警告",
|
|
"0": "多个中风险警告"
|
|
},
|
|
"suggestion": "优先处理中等风险安全问题,避免安全隐患扩大",
|
|
"itemStyle": {
|
|
"color": "#fee090"
|
|
},
|
|
"value": [
|
|
0,
|
|
2 + yadd,
|
|
ws.moderate_advisory_score
|
|
]
|
|
},
|
|
{
|
|
"name": "high_advisory_score",
|
|
"score": ws.high_advisory_score,
|
|
"description": "高风险安全警告评分",
|
|
"criteria": {
|
|
"100": "无高风险警告",
|
|
"0": "多个高风险警告"
|
|
},
|
|
"suggestion": "高风险安全问题必须立即处理,可考虑发布安全补丁版本",
|
|
"itemStyle": {
|
|
"color": "#fdae61"
|
|
},
|
|
"value": [
|
|
1,
|
|
1 + yadd,
|
|
ws.high_advisory_score
|
|
]
|
|
},
|
|
{
|
|
"name": "critical_advisory_score",
|
|
"score": ws.critical_advisory_score,
|
|
"description": "严重风险安全警告评分",
|
|
"criteria": {
|
|
"100": "无严重风险警告",
|
|
"0": "多个严重风险警告"
|
|
},
|
|
"suggestion": "严重安全问题需要紧急修复并及时通知用户更新",
|
|
"itemStyle": {
|
|
"color": "#f46d43"
|
|
},
|
|
"value": [
|
|
1,
|
|
2 + yadd,
|
|
ws.critical_advisory_score
|
|
]
|
|
},
|
|
{
|
|
"name": "stars_score",
|
|
"score": ws.stars_score,
|
|
"description": "项目Star数评分",
|
|
"criteria": {
|
|
"80-100": "Star很多",
|
|
"60-80": "Star较多",
|
|
"0-60": "Star较少"
|
|
},
|
|
"suggestion": "提高项目影响力,完善文档和功能以吸引更多用户关注",
|
|
"itemStyle": {
|
|
"color": "#93AAFC"
|
|
},
|
|
"value": [
|
|
3,
|
|
2 + yadd,
|
|
ws.stars_score
|
|
]
|
|
},
|
|
{
|
|
"name": "watching_score",
|
|
"score": ws.watching_score,
|
|
"description": "项目关注者评分",
|
|
"criteria": {
|
|
"80-100": "关注者很多",
|
|
"60-80": "关注者较多",
|
|
"0-60": "关注者较少"
|
|
},
|
|
"suggestion": "增加项目曝光度,保持更新以维持用户关注度",
|
|
"itemStyle": {
|
|
"color": "#40daff"
|
|
},
|
|
"value": [
|
|
4,
|
|
2 + yadd,
|
|
ws.watching_score
|
|
]
|
|
},
|
|
{
|
|
"name": "fork_score",
|
|
"score": ws.fork_score,
|
|
"description": "项目fork数评分",
|
|
"criteria": {
|
|
"80-100": "fork数很多",
|
|
"60-80": "fork数较多",
|
|
"0-60": "fork数较少"
|
|
},
|
|
"suggestion": "鼓励社区贡献,完善协作指南以促进fork开发",
|
|
"itemStyle": {
|
|
"color": "#73d797"
|
|
},
|
|
"value": [
|
|
5,
|
|
1 + yadd,
|
|
ws.fork_score
|
|
]
|
|
},
|
|
{
|
|
"name": "commits_number_score",
|
|
"score": ws.commits_number_score,
|
|
"description": "提交次数评分",
|
|
"criteria": {
|
|
"80-100": "提交次数很多",
|
|
"60-80": "提交次数较多",
|
|
"0-60": "提交次数较少"
|
|
},
|
|
"suggestion": "保持稳定的开发活跃度,定期提交代码更新",
|
|
"itemStyle": {
|
|
"color": "#ad73d7"
|
|
},
|
|
"value": [
|
|
6,
|
|
1 + yadd,
|
|
ws.commits_number_score
|
|
]
|
|
},
|
|
{
|
|
"name": "developer_number_score",
|
|
"score": ws.developer_number_score,
|
|
"description": "开发者数量评分",
|
|
"criteria": {
|
|
"80-100": "开发者数量很多",
|
|
"60-80": "开发者数量较多",
|
|
"0-60": "开发者数量较少"
|
|
},
|
|
"suggestion": "扩大开发团队,培养核心贡献者",
|
|
"itemStyle": {
|
|
"color": "#ee6df5"
|
|
},
|
|
"value": [
|
|
2,
|
|
2 + yadd,
|
|
ws.developer_number_score
|
|
]
|
|
},
|
|
{
|
|
"name": "release_number_score",
|
|
"score": ws.release_number_score,
|
|
"description": "发布版本数评分",
|
|
"criteria": {
|
|
"80-100": "发布版本数很多",
|
|
"60-80": "发布版本数较多",
|
|
"0-60": "发布版本数较少"
|
|
},
|
|
"suggestion": "建立规范的版本发布流程,保持稳定的迭代节奏",
|
|
"itemStyle": {
|
|
"color": "#c4b7e5"
|
|
},
|
|
"value": [
|
|
2,
|
|
1 + yadd,
|
|
ws.release_number_score
|
|
]
|
|
},
|
|
]
|
|
|
|
main_score = {
|
|
"name": "overall_maturity_score",
|
|
"score": ws.m,
|
|
"description": "总体成熟度评分",
|
|
"criteria": {
|
|
"80-100": "项目成熟,维护良好",
|
|
"60-80": "项目稳定,有待改进",
|
|
"40-60": "项目基本可用,需要加强维护",
|
|
"0-40": "项目初期或疏于维护"
|
|
},
|
|
"x_axis": ["稳健性","稳健性","稳健性","扩展性","扩展性","扩展性","维护性","维护性","维护性"],
|
|
"y_axis": ["成熟型","成熟型","成熟型","稳定型","稳定型","稳定型","成长型","成长型","成长型"],
|
|
"suggestion": "根据各项分值改进薄弱环节,提高项目整体质量"
|
|
}
|
|
|
|
return {'main_score': main_score, 'scores': scores_charts}
|
|
|
|
|
|
def fetch_data(url):
|
|
"""从给定 URL 获取数据并以 JSON 格式返回。"""
|
|
response = requests.get(url)
|
|
response.raise_for_status() # 对于错误响应抛出异常
|
|
return response.json()
|
|
|
|
|
|
@require_http_methods(['GET'])
|
|
def calculate(request):
|
|
owner = request.GET.get('owner')
|
|
repository = request.GET.get('repository')
|
|
env_url = "https://www.gitlink.org.cn"
|
|
if request.GET.get('env_url') == "testforgeplus":
|
|
env_url = "https://testforgeplus.trustie.net"
|
|
|
|
# owner = "xuos" # 替换为实际的 owner
|
|
# repository = "xiuos" # 替换为实际的 repository
|
|
|
|
# owner = "leslieder"
|
|
# repository = "nodom"
|
|
|
|
# owner = "openEuler-Ecosystem"
|
|
# repository = "openEuler-EulerOS"
|
|
|
|
# 从 API 获取数据
|
|
link1_info = f"{env_url}/api/{owner}/{repository}/detail.json"
|
|
link2_contri = f"{env_url}/api/{owner}/{repository}/contributors.json"
|
|
link3_issue = f"{env_url}/api/{owner}/{repository}/issues.json"
|
|
link4_commits = f"{env_url}/api/{owner}/{repository}/commits.json"
|
|
link_release = f"{env_url}/api/{owner}/{repository}/releases.json"
|
|
|
|
try:
|
|
software_detail = fetch_data(link1_info)
|
|
contributors = fetch_data(link2_contri)
|
|
issues = fetch_data(link3_issue)
|
|
commits = fetch_data(link4_commits)
|
|
releases = fetch_data(link_release)
|
|
|
|
# 提取必要的信息
|
|
license_info = 1 if software_detail.get("license_name") else 0
|
|
issue_opened = issues.get("open_count", 0)
|
|
issue_closed = issues.get("close_count", 0)
|
|
low_advisory = software_detail.get("advisories", {}).get("low", 0)
|
|
moderate_advisory = software_detail.get("advisories", {}).get("moderate", 0)
|
|
high_advisory = software_detail.get("advisories", {}).get("high", 0)
|
|
critical_advisory = software_detail.get("advisories", {}).get("critical", 0)
|
|
stars = software_detail.get("praises_count", 0)
|
|
watching = software_detail.get("watchers_count", 0)
|
|
forks = software_detail.get("forked_count", 0)
|
|
commits_number = commits.get("total_count")
|
|
average_day_interval = software_detail.get("average_day_interval", 0)
|
|
readme = software_detail.get("readme", False)
|
|
contributing = software_detail.get("contributing", False)
|
|
developer_number = len(contributors.get("list"))
|
|
release_number = releases.get("total_count")
|
|
user_number = software_detail.get("user_number", 0)
|
|
sum_user_score = software_detail.get("sum_user_score", 0)
|
|
|
|
except TypeError:
|
|
return JsonResponse({'data': {}})
|
|
except:
|
|
return JsonResponse({'data': {}})
|
|
# 输出每个信息
|
|
# print("许可证:", license_info)
|
|
# print("打开的问题数量:", issue_opened)
|
|
# print("关闭的问题数量:", issue_closed)
|
|
# print("低风险通报数量:", low_advisory)
|
|
# print("中风险通报数量:", moderate_advisory)
|
|
# print("高风险通报数量:", high_advisory)
|
|
# print("严重风险通报数量:", critical_advisory)
|
|
# print("星级:", stars)
|
|
# print("关注者数量:", watching)
|
|
# print("分支数量:", forks)
|
|
# print("提交数量:", commits_number)
|
|
# print("平均天数间隔:", average_day_interval)
|
|
# print("是否包含 README:", readme)
|
|
# print("是否包含贡献指南:", contributing)
|
|
# print("开发者数量:", developer_number)
|
|
# print("发布版本数量:", release_number)
|
|
# print("用户数量:", user_number)
|
|
# print("用户总评分:", sum_user_score)
|
|
|
|
# 创建 Software 实例
|
|
software = Software(
|
|
license=license_info,
|
|
issue_opened=issue_opened,
|
|
issue_closed=issue_closed,
|
|
low_advisory=low_advisory,
|
|
moderate_advisory=moderate_advisory,
|
|
high_advisory=high_advisory,
|
|
critical_advisory=critical_advisory,
|
|
stars=stars,
|
|
watching=watching,
|
|
forks=forks,
|
|
commits_number=commits_number,
|
|
average_day_interval=average_day_interval,
|
|
readme=readme,
|
|
contributing=contributing,
|
|
developer_number=developer_number,
|
|
release_number=release_number,
|
|
user_number=user_number,
|
|
sum_user_score=sum_user_score
|
|
)
|
|
|
|
update_config(software)
|
|
|
|
context = get_maturity_context()
|
|
maturity_score = maturity(software, context)
|
|
|
|
return JsonResponse({'data': maturity_score})
|
|
|
|
|
|
def update_config(software):
|
|
"""根据软件数据更新 config.ini 文件"""
|
|
config = configparser.ConfigParser()
|
|
config.read(config_file_path)
|
|
|
|
# 更新每个数据项
|
|
update_config_value(config, 'license_info', software.license)
|
|
update_config_value(config, 'issue_opened', software.issue_opened)
|
|
update_config_value(config, 'issue_closed', software.issue_closed)
|
|
update_config_value(config, 'low_advisory', software.low_advisory)
|
|
update_config_value(config, 'moderate_advisory', software.moderate_advisory)
|
|
update_config_value(config, 'high_advisory', software.high_advisory)
|
|
update_config_value(config, 'critical_advisory', software.critical_advisory)
|
|
update_config_value(config, 'stars', software.stars)
|
|
update_config_value(config, 'watching', software.watching)
|
|
update_config_value(config, 'forks', software.forks)
|
|
update_config_value(config, 'commits_number', software.commits_number)
|
|
update_config_value(config, 'developer_number', software.developer_number)
|
|
update_config_value(config, 'release_number', software.release_number)
|
|
|
|
# 保存更新后的配置
|
|
with open(config_file_path, 'w') as configfile:
|
|
config.write(configfile)
|
|
|
|
|
|
def update_config_value(config, key, value):
|
|
"""更新配置中的最大值和最小值"""
|
|
section = 'Limits' # 指定使用的节
|
|
|
|
# 获取当前最小值和最大值
|
|
current_min = config.getint(section, key + '_min')
|
|
current_max = config.getint(section, key + '_max')
|
|
|
|
# 更新最小值
|
|
if value < current_min:
|
|
config.set(section, key + '_min', str(value))
|
|
|
|
# 更新最大值
|
|
if value > current_max:
|
|
config.set(section, key + '_max', str(value))
|
|
|
|
# if __name__ == "__main__":
|
|
# owner = "xuos" # 替换为实际的 owner
|
|
# repository = "xiuos" # 替换为实际的 repository
|
|
# calculate(owner,repository) |