diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0949605 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv/ +.idea/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f0985f8 --- /dev/null +++ b/Dockerfile @@ -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"] \ No newline at end of file diff --git a/README.md b/README.md index f25c041..0228cc5 100644 --- a/README.md +++ b/README.md @@ -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**: 总体成熟度评分。 diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000..e69de29 diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..bbab9b9 --- /dev/null +++ b/manage.py @@ -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() diff --git a/maturityAnalyse/__init__.py b/maturityAnalyse/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/maturityAnalyse/__pycache__/__init__.cpython-312.pyc b/maturityAnalyse/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cd80416 Binary files /dev/null and b/maturityAnalyse/__pycache__/__init__.cpython-312.pyc differ diff --git a/maturityAnalyse/__pycache__/settings.cpython-312.pyc b/maturityAnalyse/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000..e7d497c Binary files /dev/null and b/maturityAnalyse/__pycache__/settings.cpython-312.pyc differ diff --git a/maturityAnalyse/__pycache__/urls.cpython-312.pyc b/maturityAnalyse/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000..802a85a Binary files /dev/null and b/maturityAnalyse/__pycache__/urls.cpython-312.pyc differ diff --git a/maturityAnalyse/__pycache__/views.cpython-312.pyc b/maturityAnalyse/__pycache__/views.cpython-312.pyc new file mode 100644 index 0000000..0a82151 Binary files /dev/null and b/maturityAnalyse/__pycache__/views.cpython-312.pyc differ diff --git a/maturityAnalyse/__pycache__/wsgi.cpython-312.pyc b/maturityAnalyse/__pycache__/wsgi.cpython-312.pyc new file mode 100644 index 0000000..fb85a0b Binary files /dev/null and b/maturityAnalyse/__pycache__/wsgi.cpython-312.pyc differ diff --git a/maturityAnalyse/asgi.py b/maturityAnalyse/asgi.py new file mode 100644 index 0000000..326aed1 --- /dev/null +++ b/maturityAnalyse/asgi.py @@ -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() diff --git a/maturityAnalyse/settings.py b/maturityAnalyse/settings.py new file mode 100644 index 0000000..699ce7e --- /dev/null +++ b/maturityAnalyse/settings.py @@ -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' diff --git a/maturityAnalyse/urls.py b/maturityAnalyse/urls.py new file mode 100644 index 0000000..f519308 --- /dev/null +++ b/maturityAnalyse/urls.py @@ -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"), +] diff --git a/maturityAnalyse/views.py b/maturityAnalyse/views.py new file mode 100644 index 0000000..f8ec83e --- /dev/null +++ b/maturityAnalyse/views.py @@ -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) \ No newline at end of file diff --git a/maturityAnalyse/wsgi.py b/maturityAnalyse/wsgi.py new file mode 100644 index 0000000..6a03888 --- /dev/null +++ b/maturityAnalyse/wsgi.py @@ -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() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7fbddd4 Binary files /dev/null and b/requirements.txt differ diff --git a/static/config.ini b/static/config.ini new file mode 100644 index 0000000..5721e92 --- /dev/null +++ b/static/config.ini @@ -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 + diff --git a/static/echar.js b/static/echar.js new file mode 100644 index 0000000..9ea7c92 --- /dev/null +++ b/static/echar.js @@ -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 + } + } + }] +}; \ No newline at end of file