This commit is contained in:
floraachy 2024-11-14 14:27:06 +08:00
parent 0bb4483436
commit 079ee6f0f4
19 changed files with 1703 additions and 1 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.venv/
.idea/

26
Dockerfile Normal file
View File

@ -0,0 +1,26 @@
FROM python:3.12-slim
LABEL authors="lxy"
# 设置工作目录
WORKDIR /app
# 复制依赖文件
COPY requirements.txt .
# 安装依赖
RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
# 复制项目文件
COPY . .
# 设置环境变量
ENV PYTHONUNBUFFERED=1
# 运行迁移(可选)
# RUN python manage.py migrate
# 暴露端口
EXPOSE 8080
# 启动 Django 开发服务器
CMD ["python", "manage.py", "runserver", "0.0.0.0:8080"]

318
README.md
View File

@ -1,2 +1,318 @@
# Photoshop
# 开源项目评分标准说明文档
## 1. 概述
本评分系统旨在全面评估开源项目的质量、活跃度和成熟度。评分体系包含多个维度,每个维度都有其特定的评分标准和改进建议。
## 2. 评分维度
### 2.1 基础要素评分
## 开源许可证评分
- **满分标准**: 使用标准开源许可证(MIT/Apache/GPL等) - 100分
- **不合格标准**: 无许可证 - 0分
- **改进建议**: 建议使用标准的开源许可证以明确项目的使用条款和限制
### 2.2 问题处理评分
## 未解决issue评分
- **优秀**: 未解决issue较少 (80-100分)
- **良好**: 未解决issue较多 (60-80分)
- **待改进**: 未解决issue很多 (0-60分)
- **改进建议**: 保持适度的未解决issue数量,及时处理重要issue
## 已解决issue评分
- **优秀**: 已解决issue很多 (80-100分)
- **良好**: 已解决issue较多 (60-80分)
- **待改进**: 已解决issue较少 (0-60分)
- **改进建议**: 提高issue解决率,保持良好的问题跟踪和处理机制
### 2.3 安全性评分
包含四个等级的安全警告评分:
- 低风险安全警告 (100分/0分)
- 中等风险安全警告 (100分/0分)
- 高风险安全警告 (100分/0分)
- 严重风险安全警告 (100分/0分)
每个等级的评分标准:
- **满分标准**: 无相应级别警告
- **不合格标准**: 存在多个相应级别警告
- **改进建议**: 根据风险等级优先处理安全问题,确保项目安全性
### 2.4 社区活跃度评分
## Star数评分
- **优秀**: Star很多 (80-100分)
- **良好**: Star较多 (60-80分)
- **待改进**: Star较少 (0-60分)
- **改进建议**: 提高项目影响力,完善文档和功能以吸引更多用户关注
## 关注者评分
- **优秀**: 关注者很多 (80-100分)
- **良好**: 关注者较多 (60-80分)
- **待改进**: 关注者较少 (0-60分)
- **改进建议**: 增加项目曝光度,保持更新以维持用户关注度
## Fork数评分
- **优秀**: 分支数很多 (80-100分)
- **良好**: 分支数较多 (60-80分)
- **待改进**: 分支数较少 (0-60分)
- **改进建议**: 鼓励社区贡献,完善协作指南以促进分支开发
### 2.5 开发活跃度评分
## 提交次数评分
- **优秀**: 提交次数很多 (80-100分)
- **良好**: 提交次数较多 (60-80分)
- **待改进**: 提交次数较少 (0-60分)
- **改进建议**: 保持稳定的开发活跃度,定期提交代码更新
## 开发者数量评分
- **优秀**: 开发者数量很多 (80-100分)
- **良好**: 开发者数量较多 (60-80分)
- **待改进**: 开发者数量较少 (0-60分)
- **改进建议**: 扩大开发团队,培养核心贡献者
## 发布版本数评分
- **优秀**: 发布版本数很多 (80-100分)
- **良好**: 发布版本数较多 (60-80分)
- **待改进**: 发布版本数较少 (0-60分)
- **改进建议**: 建立规范的版本发布流程,保持稳定的迭代节奏
## 3. 总体成熟度评分
- **优秀**: 项目成熟,维护良好 (80-100分)
- **良好**: 项目稳定,有待改进 (60-80分)
- **一般**: 项目基本可用,需要加强维护 (40-60分)
- **待改进**: 项目初期或疏于维护 (0-40分)
- **改进建议**: 根据各项分值改进薄弱环节,提高项目整体质量
## 4. 评分使用建议
1. 定期进行评分,跟踪项目发展状况
2. 针对低分项优先改进
3. 关注安全警告,及时处理安全问题
4. 持续完善文档和社区建设
5. 保持稳定的更新和维护频率
## 5.接口文档
### 接口名称
项目成熟度分析接口
### 接口路径
```
/maturityAnalyse/
```
### 请求方法
```
GET
```
### 请求参数
| 参数名 | 类型 | 是否必填 | 描述 |
| ---------- | ------ | -------- | ------------------ |
| owner | string | 是 | GitLink 仓库拥有者 |
| repository | string | 是 | GitLink 仓库名称 |
### 示例请求
```
bash
复制代码
GET /maturityAnalyse/?owner=xuos&repository=xiuos
```
### 返回格式
返回数据为 JSON 格式,结构如下:
```
{
"data": {
"scores": {
"license_score": {
"score": 100,
"description": "开源许可证评分",
"criteria": {
"100": "使用标准开源许可证(MIT/Apache/GPL等)",
"0": "无许可证"
},
"suggestion": "建议使用标准的开源许可证以明确项目的使用条款和限制"
},
"open_issue_score": {
"score": 1,
"description": "未解决issue评分",
"criteria": {
"80-100": "未解决issue较少",
"60-80": "未解决issue较多",
"0-60": "未解决issue很多"
},
"suggestion": "保持适度的未解决issue数量,及时处理重要issue"
},
"closed_issue_score": {
"score": 0,
"description": "已解决issue评分",
"criteria": {
"80-100": "已解决issue很多",
"60-80": "已解决issue较多",
"0-60": "已解决issue较少"
},
"suggestion": "提高issue解决率,保持良好的问题跟踪和处理机制"
},
"low_advisory_score": {
"score": 100,
"description": "低风险安全警告评分",
"criteria": {
"100": "无低风险警告",
"0": "多个低风险警告"
},
"suggestion": "定期检查并修复安全隐患,即使是低风险问题也应重视"
},
"moderate_advisory_score": {
"score": 100,
"description": "中等风险安全警告评分",
"criteria": {
"100": "无中等风险警告",
"0": "多个中风险警告"
},
"suggestion": "优先处理中等风险安全问题,避免安全隐患扩大"
},
"high_advisory_score": {
"score": 100,
"description": "高风险安全警告评分",
"criteria": {
"100": "无高风险警告",
"0": "多个高风险警告"
},
"suggestion": "高风险安全问题必须立即处理,可考虑发布安全补丁版本"
},
"critical_advisory_score": {
"score": 100,
"description": "严重风险安全警告评分",
"criteria": {
"100": "无严重风险警告",
"0": "多个严重风险警告"
},
"suggestion": "严重安全问题需要紧急修复并及时通知用户更新"
},
"stars_score": {
"score": 42,
"description": "项目Star数评分",
"criteria": {
"80-100": "Star很多",
"60-80": "Star较多",
"0-60": "Star较少"
},
"suggestion": "提高项目影响力,完善文档和功能以吸引更多用户关注"
},
"watching_score": {
"score": 100,
"description": "项目关注者评分",
"criteria": {
"80-100": "关注者很多",
"60-80": "关注者较多",
"0-60": "关注者较少"
},
"suggestion": "增加项目曝光度,保持更新以维持用户关注度"
},
"fork_score": {
"score": 53,
"description": "项目分支数评分",
"criteria": {
"80-100": "分支数很多",
"60-80": "分支数较多",
"0-60": "分支数较少"
},
"suggestion": "鼓励社区贡献,完善协作指南以促进分支开发"
},
"commits_number_score": {
"score": 100,
"description": "提交次数评分",
"criteria": {
"80-100": "提交次数很多",
"60-80": "提交次数较多",
"0-60": "提交次数较少"
},
"suggestion": "保持稳定的开发活跃度,定期提交代码更新"
},
"developer_number_score": {
"score": 100,
"description": "开发者数量评分",
"criteria": {
"80-100": "开发者数量很多",
"60-80": "开发者数量较多",
"0-60": "开发者数量较少"
},
"suggestion": "扩大开发团队,培养核心贡献者"
},
"release_number_score": {
"score": 100,
"description": "发布版本数评分",
"criteria": {
"80-100": "发布版本数很多",
"60-80": "发布版本数较多",
"0-60": "发布版本数较少"
},
"suggestion": "建立规范的版本发布流程,保持稳定的迭代节奏"
},
"overall_maturity_score": {
"score": 76.61538461538461,
"description": "总体成熟度评分",
"criteria": {
"80-100": "项目成熟,维护良好",
"60-80": "项目稳定,有待改进",
"40-60": "项目基本可用,需要加强维护",
"0-40": "项目初期或疏于维护"
},
"suggestion": "根据各项分值改进薄弱环节,提高项目整体质量"
}
}
}
}
```
### 返回字段说明
- data
: 返回的数据对象。
- scores
: 各项评分的详细信息。
- **license_score**: 开源许可证评分。
- **open_issue_score**: 未解决 issue 的评分。
- **closed_issue_score**: 已解决 issue 的评分。
- **low_advisory_score**: 低风险安全警告评分。
- **moderate_advisory_score**: 中等风险安全警告评分。
- **high_advisory_score**: 高风险安全警告评分。
- **critical_advisory_score**: 严重风险安全警告评分。
- **stars_score**: 项目 Star 数评分。
- **watching_score**: 项目关注者评分。
- **fork_score**: 项目分支数评分。
- **commits_number_score**: 提交次数评分。
- **developer_number_score**: 开发者数量评分。
- **release_number_score**: 发布版本数评分。
- **overall_maturity_score**: 总体成熟度评分。

0
db.sqlite3 Normal file
View File

22
manage.py Normal file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'maturityAnalyse.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
maturityAnalyse/asgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
ASGI config for maturityAnalyse project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'maturityAnalyse.settings')
application = get_asgi_application()

124
maturityAnalyse/settings.py Normal file
View File

@ -0,0 +1,124 @@
"""
Django settings for maturityAnalyse project.
Generated by 'django-admin startproject' using Django 5.1.2.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-bk5uuf*=u0@dnlpicrk%(7#20iyez^yeh9yp1wau4r_7653vev'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'maturityAnalyse.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates']
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'maturityAnalyse.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

25
maturityAnalyse/urls.py Normal file
View File

@ -0,0 +1,25 @@
"""
URL configuration for maturityAnalyse project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from maturityAnalyse.views import calculate
urlpatterns = [
path('admin/', admin.site.urls),
path('maturityAnalyse/', calculate , name = "calculate"),
]

611
maturityAnalyse/views.py Normal file
View File

@ -0,0 +1,611 @@
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)

16
maturityAnalyse/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for maturityAnalyse project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'maturityAnalyse.settings')
application = get_wsgi_application()

BIN
requirements.txt Normal file

Binary file not shown.

28
static/config.ini Normal file
View File

@ -0,0 +1,28 @@
[Limits]
license_info_min = 0
license_info_max = 1
issue_opened_min = 0
issue_opened_max = 57
issue_closed_min = 0
issue_closed_max = 0
low_advisory_min = 0
low_advisory_max = 0
moderate_advisory_min = 0
moderate_advisory_max = 0
high_advisory_min = 0
high_advisory_max = 0
critical_advisory_min = 0
critical_advisory_max = 0
stars_min = 0
stars_max = 226
watching_min = 0
watching_max = 56
forks_min = 0
forks_max = 2668
commits_number_min = 17
commits_number_max = 2083
developer_number_min = 1
developer_number_max = 20
release_number_min = 0
release_number_max = 2

516
static/echar.js Normal file
View File

@ -0,0 +1,516 @@
// prettier-ignore
$.get('https://testbd.trustie.net/maturityAnalyse/?owner=GitLink&repository=gitea_hat').done(function(data) {
var e = data.data.scores
});
var step = 2;
var yadd= step * 3 -1
e=[
{
"name": "license_score",
"score": 0,
"description": "开源许可证评分",
"criteria": {
"0": "无许可证",
"100": "使用标准开源许可证(MIT/Apache/GPL等)"
},
"suggestion": "建议使用标准的开源许可证以明确项目的使用条款和限制",
"itemStyle": {
"color": "#74add1"
},
"value": [
8,
1+yadd,
0
]
},
{
"name": "open_issue_score",
"score": 100,
"description": "未解决issue评分",
"criteria": {
"80-100": "未解决issue较少",
"60-80": "未解决issue较多",
"0-60": "未解决issue很多"
},
"suggestion": "保持适度的未解决issue数量,及时处理重要issue",
"itemStyle": {
"color": "#74add1"
},
"value": [
3,
1+yadd,
100
]
},
{
"name": "closed_issue_score",
"score": 99,
"description": "已解决issue评分",
"criteria": {
"80-100": "已解决issue很多",
"60-80": "已解决issue较多",
"0-60": "已解决issue较少"
},
"suggestion": "提高issue解决率,保持良好的问题跟踪和处理机制",
"itemStyle": {
"color": "#74add1"
},
"value": [
3,
2+yadd,
99
]
},
{
"name": "low_advisory_score",
"score": 100,
"description": "低风险安全警告评分",
"criteria": {
"0": "多个低风险警告",
"100": "无低风险警告"
},
"suggestion": "定期检查并修复安全隐患,即使是低风险问题也应重视",
"itemStyle": {
"color": "#ffffbf"
},
"value": [
5,
1+yadd,
100
]
},
{
"name": "moderate_advisory_score",
"score": 100,
"description": "中等风险安全警告评分",
"criteria": {
"0": "多个中风险警告",
"100": "无中等风险警告"
},
"suggestion": "优先处理中等风险安全问题,避免安全隐患扩大",
"itemStyle": {
"color": "#fee090"
},
"value": [
5,
2+yadd,
100
]
},
{
"name": "high_advisory_score",
"score": 100,
"description": "高风险安全警告评分",
"criteria": {
"0": "多个高风险警告",
"100": "无高风险警告"
},
"suggestion": "高风险安全问题必须立即处理,可考虑发布安全补丁版本",
"itemStyle": {
"color": "#fdae61"
},
"value": [
4,
1+yadd,
100
]
},
{
"name": "critical_advisory_score",
"score": 100,
"description": "严重风险安全警告评分",
"criteria": {
"0": "多个严重风险警告",
"100": "无严重风险警告"
},
"suggestion": "严重安全问题需要紧急修复并及时通知用户更新",
"itemStyle": {
"color": "#f46d43"
},
"value": [
4,
2+yadd,
100
]
},
{
"name": "stars_score",
"score": 0,
"description": "项目Star数评分",
"criteria": {
"80-100": "Star很多",
"60-80": "Star较多",
"0-60": "Star较少"
},
"suggestion": "提高项目影响力,完善文档和功能以吸引更多用户关注",
"itemStyle": {
"color": "#93AAFC"
},
"value": [
0,
1+yadd,
100
]
},
{
"name": "watching_score",
"score": 100,
"description": "项目关注者评分",
"criteria": {
"80-100": "关注者很多",
"60-80": "关注者较多",
"0-60": "关注者较少"
},
"suggestion": "增加项目曝光度,保持更新以维持用户关注度",
"itemStyle": {
"color": "#93AAFC"
},
"value": [
0,
2+yadd,
100
]
},
{
"name": "fork_score",
"score": 0,
"description": "项目分支数评分",
"criteria": {
"80-100": "分支数很多",
"60-80": "分支数较多",
"0-60": "分支数较少"
},
"suggestion": "鼓励社区贡献,完善协作指南以促进分支开发",
"itemStyle": {
"color": "#93AAFC"
},
"value": [
1,
1+yadd,
0
]
},
{
"name": "commits_number_score",
"score": 28,
"description": "提交次数评分",
"criteria": {
"80-100": "提交次数很多",
"60-80": "提交次数较多",
"0-60": "提交次数较少"
},
"suggestion": "保持稳定的开发活跃度,定期提交代码更新",
"itemStyle": {
"color": "#93AAFC"
},
"value": [
2,
1+yadd,
28
]
},
{
"name": "developer_number_score",
"score": 0,
"description": "开发者数量评分",
"criteria": {
"80-100": "开发者数量很多",
"60-80": "开发者数量较多",
"0-60": "开发者数量较少"
},
"suggestion": "扩大开发团队,培养核心贡献者",
"itemStyle": {
"color": "#93AAFC"
},
"value": [
2,
2+yadd,
28
]
},
{
"name": "release_number_score",
"score": 0,
"description": "发布版本数评分",
"criteria": {
"80-100": "发布版本数很多",
"60-80": "发布版本数较多",
"0-60": "发布版本数较少"
},
"suggestion": "建立规范的版本发布流程,保持稳定的迭代节奏",
"itemStyle": {
"color": "#93AAFC"
},
"value": [
1,
2+yadd,
0
]
}
]
var t = [
"活跃度",
"活跃度",
"活跃度",
"质量安全",
"质量安全",
"质量安全",
"基础要素",
"基础要素",
"基础要素"
];
// 基础要素:开源许可证评分,
// 质量: 未解决issue评分,已解决issue评分, 安全性评分
// 活跃度: Star数评分,关注者评分,Fork数评分,提交次数评分,开发者数量评分,发布版本数评分
// var i = [
// "协作",
// "协作",
// "协作",
// "社区活跃度",
// "社区活跃度",
// "社区活跃度",
// "开发活跃度",
// "开发活跃度",
// "开发活跃度"
// ];
var i = [
"成熟型",
"成熟型",
"成熟型",
"稳定型",
"稳定型",
"稳定型",
"成长型",
"成长型",
"成长型"
];
// var i = [
// "项目成熟,维护良好",
// "项目成熟,维护良好",
// "项目成熟,维护良好",
// "项目稳定,有待改进",
// "项目稳定,有待改进",
// "项目稳定,有待改进",
// "项目基本可用,需要加强维护",
// "项目基本可用,需要加强维护",
// "项目基本可用,需要加强维护",
// "项目初期或疏于维护",
// "项目初期或疏于维护",
// "项目初期或疏于维护",
// // "质量",
// // "基础要素",
// // "基础要素",
// // "基础要素",
// ];
var a = 3;
var n = 3;
var o = false;
// prettier-ignore
// var days = ['', 'Friday', '',
// 'Wednesday', '', '', 'Sunday'];
// prettier-ignore
const l = o ? "white" : "#333"
, r = o ? "rgba(255,255,255,0.5)" : "#aaa";
// var data = [ [0, 16, 4], [0, 17, 6], [0, 18, 4], [0, 19, 4], [0, 20, 3], [0, 21, 3], [0, 22, 2], [0, 23, 5], [1, 0, 7], [1, 1, 0], [1, 2, 0]];
option = {
tooltip: {
textStyle: {
fontWeight: "bolder"
},
formatter: e => "mock" === e.data.validity ? e.name + ": \u5373\u5c06\u4e0a\u7ebf" : e.data.description + ": " + e.data.value[2]
},
visualMap: {
max: 20,
inRange: {
color: [
'#313695',
'#4575b4',
'#74add1',
'#abd9e9',
'#e0f3f8',
'#ffffbf',
'#fee090',
'#fdae61',
'#f46d43',
'#d73027',
'#a50026'
]
}
},
xAxis3D: {
name: " ",
type: "category",
data: t,
interval: 2,
axisLine: {
lineStyle: {
width: 1,
color: l
}
},
splitLine: {
show: !0,
interval: a - 1
},
axisLabel: {
interval: a - 1,
textStyle: {
color: r,
padding: [10, 10, 0, 0]
}
},
axisTick: {
interval: a - 1,
length: 1
}
},
yAxis3D: {
name: " ",
type: "category",
data: i,
axisLine: {
lineStyle: {
width: 1,
color: l
}
},
axisTick: {
interval: n - 1,
length: 0
},
splitArea: {
show: !0,
areaStyle: {
color: ["rgba(255,247,207,0.5)", "rgba(255,231,231,0.5)", "rgba(226,226,226,0.5)"]
}
},
axisLabel: {
interval: n - 1,
textStyle: {
lineHeight: 2,
color: r,
padding: [10, 0, 0, 0]
}
},
splitLine: {
show: !0,
interval: n - 1
}
},
zAxis3D: {
name: " ",
type: "value",
splitNumber: 4,
axisLine: {
lineStyle: o ? {
width: 1,
opacity: .2,
color: l
} : {
width: .1,
opacity: .5,
color: l
}
},
splitArea: {
show: !0,
areaStyle: {
color: ["#ffffff", "#ffffff", "#ffffff"]
}
},
axisLabel: {
show: !1
},
axisTick: {
show: !1
}
},
grid3D: {
axisLine: {
interval: 1
},
axisPointer: {
lineStyle: {
opacity: .2
},
label: {
show: !1
}
},
splitLine: {
interval: 2
},
viewControl: {
projection: "perspective",
zoomSensitivity: 0,
rotateSensitivity: [1, 0],
distance: 380,
alpha: 20,
beta: 25
},
top: -40,
boxWidth: 150,
boxDepth: 150,
environment: "none",
light: {
main: {
intensity: .8,
alpha: 50
},
ambient: {
intensity: .5
}
}
},
series: [{
type: "bar3D",
data: e,
shading: "realistic",
bevelSize: .1,
bevelSmoothness: 2,
barSize: 10,
label: {
show: !1,
fontSize: 12,
fontWeight: 500,
borderWidth: 1,
color: "#333",
distance: -20,
formatter: function (e) {
for (var t = e.data.description, i = 2, a = Math.ceil(t.length / i), n = "\n", o = 0; o < a; o++) {
var l = o * i
, r = l + i;
n += t.substring(l, r) + "\n"
}
return n
}
},
itemStyle: {
opacity: .95
},
emphasis: {
label: {
show: !1
},
itemStyle: {
color: "#FFB800",
opacity: .7
}
}
}]
};