diff --git a/README.md b/README.md index 228957168dcb6bacf6cb67f1b3843b694a590e3c..a8e121315b26cbb76525aeb03e97518a55add23f 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,6 @@ # Final Project: Gitee Repository Analysis & Visualization # 期末项目:Gitee仓库分析与可视化 -This final project requires your team to programmatically analyze multiple Gitee repositories chosen by your team. - -本期末项目要求你的团队以编程方式分析团队选择的多个Gitee仓库。 - -You will use Python with the GitPython, Seaborn, and Matplotlib libraries to extract data, generate insightful visualizations, and produce comprehensive Markdown reports. - -你将使用Python配合GitPython、Seaborn和Matplotlib库来提取数据、生成深度可视化图表并制作全面的Markdown报告。 - -**The project emphasizes:** -**项目重点强调:** - -- Deep repository analysis -- 深度仓库分析 -- Effective data visualization -- 高效数据可视化 -- Comparative analysis across projects -- 跨项目对比分析 -- Team collaboration -- 团队协作 -- Adherence to proper Git workflow -- 遵循规范的Git工作流程 -- Creative exploration beyond core requirements -- 超越核心需求的创造性探索 - -**25% of your grade will be based on creative extensions beyond base requirements, such as:** -**25%的分数将基于超出基础要求的创造性扩展,例如:** - -- Implementing additional insightful statistics -- 实现额外的深度统计指标 -- Creating supplementary visualizations -- 创建补充性可视化图表 -- Enhancing report style and presentation -- 增强报告样式和呈现效果 - -You may refer to `https://gitee.com/ArsalaBangash/RepoReport` for sample code and reports. - -你可参考 `https://gitee.com/ArsalaBangash/RepoReport` 获取示例代码和报告。 - ## **Repository Selection** ## **仓库选择** @@ -46,221 +8,41 @@ You may refer to `https://gitee.com/ArsalaBangash/RepoReport` for sample code an ### **团队选择**:你的团队将选择要分析的Gitee仓库。 -* **2-member groups**: Select and analyze **two (2)** distinct Gitee repositories. - **2人小组**:选择并分析**两个(2)**不同的Gitee仓库。 * **3-member groups**: Select and analyze **three (3)** distinct Gitee repositories. **3人小组**:选择并分析**三个(3)**不同的Gitee仓库。 - -### **Selection Criteria**: Chosen repositories must: -### **选择标准**:所选仓库必须满足: - -* Be publicly accessible on Gitee. - 在Gitee上公开可访问。 -* Have at least 1 year of activity. - 拥有至少1年的活跃记录。 -* Contain over 250 commits. - 包含超过250次提交。 -* Have more than 10 authors - 拥有超过10位作者 - -## Project README Content -## 项目README内容 - -Your team's main project `README.md` (the one in your forked assignment repository) must be updated to include the following sections, written by your team: - -团队的主要项目`README.md`(位于你fork的任务仓库中)必须更新为包含以下由团队撰写的部分: - ### 1. Chosen Repositories ### 1. 所选仓库 - -* List the full Gitee URL for each chosen repository. - 列出每个所选仓库的完整Gitee URL。 -* Specify the short `reponame` your team will use for each repository (e.g., `captcha`, `jeepay`) for file naming purposes. This `reponame` must be used consistently. - 为每个仓库指定团队将使用的简短`reponame`(例如`captcha`, `jeepay`)用于文件命名,该`reponame`必须保持一致性。 -* Provide a brief (2-3 sentences per repository) justification for why your team selected each repository. - 为每个仓库提供简要选择理由(每仓库2-3句话)。 + 1.https://gitee.com/log4j/pig + 2.https://gitee.com/y_project/RuoYi + 3.https://gitee.com/mindskip/uexam +### Abbreviation of subsequent warehouses +### 后续仓库简称 + 1.report-pig = https://gitee.com/log4j/pig + 2.report-RuoYi = https://gitee.com/y_project/RuoYi + 3.report-uexam = https://gitee.com/mindskip/uexam +### Reasons for choosing a warehouse +### 选择仓库的理由 + 1.select https://gitee.com/log4j/pig This warehouse is designed as a permission management system and meets the requirements of the selection system + 2.chose https://gitee.com/y_project/RuoYi because its content is a permission management system, which is easy to read and understand, has a simple and beautiful interface, and is convenient for analysis. + 3.chose https://gitee.com/mindskip/uexam because this is an online exam system that is relevant to real life, very useful, and simple to analyze data ### 2. Comparative Analysis & Key Findings -### 2. 对比分析与关键发现 - -* After all scripts have run and reports/visualizations are generated, your team will collaboratively write this section. - 所有脚本运行且报告/可视化生成后,团队需协作撰写此部分。 -* Discuss any interesting patterns, significant differences, or similarities observed across the analyzed repositories, drawing upon the data in `comparison_report.md` and the individual repository reports. - 基于`comparison_report.md`和单个仓库报告的数据,讨论跨仓库分析中发现的任何有趣模式、显著差异或相似点。 -* Highlight 2-3 key insights or major takeaways from your overall project analysis. - 突出整个项目分析中的2-3个关键洞察或主要结论。 - -## **Project Structure and Script Execution** -## **项目结构与脚本执行** - -* **Individual Repository Analysis Scripts (`report-{reponame}.py`)**: For each chosen repository, create a Python script named `report-{reponame}.py`. This script contains the logic to analyze one repository and generate its report. - **独立仓库分析脚本(`report-{reponame}.py`)**:为每个所选仓库创建Python脚本,包含分析单个仓库并生成报告的代码逻辑。 -* **Individual Repository Reports (`report-{reponame}.md`)**: Each `report-{reponame}.py` script must generate a corresponding Markdown report named `report-{reponame}.md`. - **独立仓库报告(`report-{reponame}.md`)**:每个`report-{reponame}.py`脚本必须生成对应的Markdown报告。 -* **Cross-Repository Comparison Script (`compare.py`)**: Your team will create one Python script named `compare.py`. This script contains the logic to perform cross-repository comparisons. - **跨仓库对比脚本(`compare.py`)**:团队需创建Python脚本,包含执行跨仓库对比的逻辑。 -* **Cross-Repository Comparison Report (`comparison_report.md`)**: The `compare.py` script must generate a Markdown report named `comparison_report.md`. - **跨仓库对比报告(`comparison_report.md`)**:`compare.py`脚本必须生成Markdown格式的对比报告。 -* **Visualizations**: All generated chart images must be stored in a `visuals/` subdirectory within your project. - **可视化图表**:所有生成的图表必须存储在项目的`visuals/`子目录中。 - -## **Team Implementation and Task Division (General Guidance)** -## **团队实施与任务分工(通用指南)** - -* **For 3-member groups**: Each member is primarily responsible for developing the analysis logic and report generation for one `report-{reponame}.py` script and its corresponding `report-{reponame}.md`. The `compare.py` script and `comparison_report.md` should be collaborative efforts. - **3人小组**:每位成员主要负责开发一个`report-{reponame}.py`脚本及其对应报告的分析逻辑,`compare.py`脚本和对比报告需协作完成。 -* **For 2-member groups**: Each member is primarily responsible for developing the analysis logic and report generation for one `report-{reponame}.py` script and its corresponding `report-{reponame}.md`. The `compare.py` script and `comparison_report.md` should be collaborative efforts. - **2人小组**:每位成员主要负责开发一个`report-{reponame}.py`脚本及其对应报告的分析逻辑,`compare.py`脚本和对比报告需协作完成。 -* The `.gitignore` file and overall project structure are shared responsibilities. - `.gitignore`文件和整体项目结构为共同责任。 -* The content for the main project `README.md` ("Chosen Repositories" and "Overall Comparative Analysis") is a collaborative writing effort. - 主项目`README.md`的内容("所选仓库"和"整体对比分析")需协作撰写。 -* All code contributions (Python scripts, `.gitignore` updates, etc.), generated report files (`.md`), and generated image files (`visuals/*.png`) must be submitted via Pull Requests (PRs) within your group's forked repository. - 所有代码贡献(Python脚本、`.gitignore`更新等)、生成的报告文件(`.md`)和图像文件(`visuals/*.png`)必须通过Pull Requests(PR)提交到团队fork的仓库。 -* Develop new work on feature branches. - 在特性分支(feature branches)上进行开发。 -* Ensure clear commit messages and PR descriptions. - 确保清晰的提交消息和PR描述。 - -## **Visualization Requirements** -## **可视化要求** - -- All generated charts must be saved as PNG files in the `visuals/` subdirectory. - 所有生成的图表必须保存为PNG格式到`visuals/`子目录。 -- Chart titles, axis labels, legends (where appropriate), and overall presentation should be clear, professional, and informative. - 图表标题、坐标轴标签、图例(如适用)及整体呈现应清晰、专业且信息明确。 - -#### **A. For Each Individual Repository Report (`report-{reponame}.md`)**: -#### **A. 每个独立仓库报告(`report-{reponame}.md`)**: - -1. **Top 10 Contributors:** - **前10位贡献者:** - * **Description:** A horizontal bar chart displaying the top 10 contributors to the repository. - **描述:** 展示仓库前10位贡献者的水平条形图。 - * **Y-axis:** Contributor names. - **Y轴:** 贡献者姓名。 - * **X-axis:** Number of commits made by each contributor. - **X轴:** 每位贡献者的提交次数。 - * **Ordering:** Contributors should be ordered from most commits to fewest commits (descending). - **排序:** 贡献者按提交次数从多到少降序排列。 - -2. **Commit Activity Over Last 12 Recorded Months:** - **过去12个月提交活动:** - * **Description:** A line chart illustrating the trend of commit activity per month. - **描述:** 展示每月提交活动趋势的折线图。 - * **X-axis:** Months, covering the last 12 months for which commit data exists in the repository. Format as "Month YYYY" (e.g., "Jun 2025"). - **X轴:** 月份(仓库中存在提交数据的最近12个月),格式为"月 年"(如"Jun 2025")。 - * **Y-axis:** Total number of commits made in each respective month. - **Y轴:** 每月提交总数。 - * **Markers:** Data points on the line should be clearly marked. - **标记:** 折线上的数据点需清晰标注。 - -3. **Commit Activity by Day of the Week:** - **按星期提交活动分布:** - * **Description:** A bar chart showing the distribution of total commits across the days of the week. - **描述:** 展示一周中各天提交总数分布的条形图。 - * **X-axis:** Days of the week (e.g., Monday, Tuesday, ..., Sunday). - **X轴:** 星期几(周一至周日)。 - * **Y-axis:** Total number of commits made on each respective day of the week over the project's history. - **Y轴:** 项目历史上每周各天的提交总数。 - -4. **Cumulative Commit Growth:** - **累计提交增长:** - * **Description:** A line chart depicting the cumulative growth of commits over the entire history of the project. - **描述:** 展示项目整个历史中提交累计增长的折线图。 - * **X-axis:** Time (dates of commits, chronologically ordered). - **X轴:** 时间(按时间顺序排列的提交日期)。 - * **Y-axis:** The cumulative (total) number of commits up to each point in time. This line should always be non-decreasing. - **Y轴:** 截至每个时间点的累计提交总数,该线应始终保持非递减。 - -5. **Distribution of Lines Changed Per Commit:** - **每次提交的代码行变更分布:** - * **Description:** A box plot summarizing the distribution of the total number of lines (added + deleted) changed per commit. - **描述:** 总结每次提交变更总行数(添加+删除)分布的箱线图。 - * **Y-axis:** Number of lines changed. - **Y轴:** 变更行数。 - * **Details:** The box should represent the interquartile range (IQR), with a line for the median. Whiskers should extend to show the range of the data, but consider excluding extreme outliers from the main plot scale for better readability of the core distribution (e.g., by using Seaborn's `showfliers=False` parameter or similar techniques if outliers heavily skew the plot). - **细节:** 箱体表示四分位距(IQR),中线为众数。须线应延伸展示数据范围,但为提升核心分布可读性,可考虑排除极端离群值(例如使用Seaborn的`showfliers=False`参数)。 - -6. **Distribution of Files Changed Per Commit:** - **每次提交的文件变更分布:** - * **Description:** A box plot summarizing the distribution of the number of files changed per commit. - **描述:** 总结每次提交变更文件数分布的箱线图。 - * **Y-axis:** Number of files changed. - **Y轴:** 变更文件数。 - * **Details:** Similar to the lines changed box plot, this should clearly show the median, IQR, and range, potentially managing extreme outliers for visual clarity. - **细节:** 类似代码行变更箱线图,需清晰展示众数、IQR和范围,可管理极端离群值以提升可视化清晰度。 - -#### **B. For `comparison_report.md`**: -#### **B. 对比报告(`comparison_report.md`)**: - -1. **Cumulative Commit Growth Comparison:** - **累计提交增长对比:** - * **Description:** A single line chart comparing the cumulative commit growth of all analyzed repositories. - **描述:** 对比所有分析仓库累计提交增长的折线图。 - * **X-axis:** Time (dates, normalized or aligned appropriately if start dates differ significantly, or plotted against a common time axis). - **X轴:** 时间(若起始日期差异显著则需标准化对齐,或使用统一时间轴)。 - * **Y-axis:** The cumulative (total) number of commits. - **Y轴:** 累计提交总数。 - * **Legend:** A clear legend is required to distinguish the lines for each repository. - **图例:** 需清晰图例区分各仓库曲线。 - -2. **Total Commits Comparison:** - **提交总数对比:** - * **Description:** A grouped bar chart comparing the total number of commits for each analyzed repository. - **描述:** 对比各仓库提交总数的分组条形图。 - * **X-axis:** Repository names - **X轴:** 仓库名称 - * **Y-axis:** Total number of commits. - **Y轴:** 提交总数。 - -## **Technical Requirements** -## **技术要求** - -### **Core Libraries** -### **核心库** - -* You **must** use `git` (GitPython) Python library to interact with Git repositories. - 必须使用`git`(GitPython)库操作Git仓库。 -* You **must** use `seaborn` and `matplotlib` for generating all data visualizations. - 必须使用`seaborn`和`matplotlib`生成所有数据可视化。 -* You **must** use `numpy` for statistical calculations if needed. - 如需要统计计算必须使用`numpy`。 -* You will construct the Markdown content directly using Python strings. - 需直接使用Python字符串构建Markdown内容。 - -### **Repository Cloning** -### **仓库克隆** +### 对比分析与关键发现 + +![Normalized cumulative submission comparison](visuals/cumulative_commits_comparison_normalized.png) -* Your Python scripts will need to clone the target Gitee repositories. - Python脚本需克隆目标Gitee仓库。 -* These cloned repositories should reside as subdirectories within your main project folder. - 克隆的仓库应作为子目录存放在主项目文件夹中。 +### Key Observations for Standardized Comparisons: -### **Data Aggregation for Comparison** -### **对比数据聚合** +- Slope: Represents the average commit rate over time. A steeper slope indicates a higher level of activity. +- Curve Shape: A concave curve indicates a slowdown in activity, and a convex curve indicates an acceleration of activity. +- **Final Height**: Represents the total number of submissions for the most recent available date. -* The `compare.py` script must programmatically access key summary statistics from each of the repositories analyzed by the team. Your individual `report-{reponame}.py` scripts must save small, structured temporary data files with key metrics that `compare.py` can then use. These temporary files **must be ignored by `.gitignore`**. - `compare.py`脚本必须以编程方式访问团队分析的各仓库关键统计指标。单个`report-{reponame}.py`脚本需保存带关键指标的结构化临时数据文件供`compare.py`使用,这些临时文件必须被`.gitignore`忽略。 +![Comparison of total number of submissions](visuals/total_commits_comparison.png) -### **Visualization Requirements** -### **可视化要求** +### Key observations: -* All charts must be saved as PNG files in the `visuals/` subdirectory. - 所有图表必须保存为PNG格式到`visuals/`子目录。 -* Chart titles, axis labels, legends (where appropriate), and overall presentation should be clear, professional, and informative. - 图表标题、坐标轴标签、图例(如适用)及整体呈现应清晰、专业且信息明确。 +- Submissions: Represents the total development effort in commits. +- Activity Level: A higher number of commits is usually indicative of more active development. +Lifespan: Older projects tend to have more commits, but this depends on the speed of development. -### **`.gitignore` Configuration** -### **`.gitignore`配置** -* Your project must include a `.gitignore` file. - 项目必须包含`.gitignore`文件。 -* This file must be configured to ignore: - 该文件必须配置为忽略: - * The subdirectories containing the cloned repositories (e.g., `jeepay/`). - 包含克隆仓库的子目录(如`jeepay/`)。 - * The `.idea/` directory (used by PyCharm). - PyCharm使用的`.idea/`目录。 - * Any temporary intermediate data files - 所有临时中间数据文件 \ No newline at end of file diff --git a/report-pig.md b/report-pig.md new file mode 100644 index 0000000000000000000000000000000000000000..59da97ec07a0df3e8b2619a35ddbbdd20db6bbd8 --- /dev/null +++ b/report-pig.md @@ -0,0 +1,104 @@ +# pig Project Data Analysis + +--- + +**Start Date:** 2019-02-01 21:06:59 + +**Latest Update:** 2025-06-05 17:38:45 + +[**Commits:** 1935](https://gitee.com/log4j/pig/commits/master) + +[**Branches:** 6](https://gitee.com/log4j/pig/branches) + +[**Authors:** 156](https://gitee.com/log4j/pig/contributors) + +## Branches + +- [master](https://gitee.com/log4j/pig/tree/master) + +- [1.x](https://gitee.com/log4j/pig/tree/1.x) + +- [dev](https://gitee.com/log4j/pig/tree/dev) + +- [jdk17](https://gitee.com/log4j/pig/tree/jdk17) + +- [jdk17-dev](https://gitee.com/log4j/pig/tree/jdk17-dev) + +- [next-sa](https://gitee.com/log4j/pig/tree/next-sa) + +## Top 10 Authors + +1. [lbw](https://gitee.com/lbw): 486 commits + +2. [冷冷](https://gitee.com/冷冷): 469 commits + +3. [lengleng](https://gitee.com/lengleng): 159 commits + +4. [renovate[bot]](https://gitee.com/renovate[bot]): 143 commits + +5. [lishangbu](https://gitee.com/lishangbu): 75 commits + +6. [pig4cloud.com](https://gitee.com/pig4cloud.com): 33 commits + +7. [PIGAI](https://gitee.com/PIGAI): 30 commits + +8. [付绪壮](https://gitee.com/付绪壮): 29 commits + +9. [qraddx](https://gitee.com/qraddx): 29 commits + +10. [wjie](https://gitee.com/wjie): 28 commits + +## Top 10 Authors Bar Chart + +![Top 10 Authors](visuals/top_authors.png) + +## Commit Stats + +**Average Lines Changed / Commit:** 424 + +**Median Lines Changed / Commit:** 6 + +**Average Files Changed / Commit:** 8 + +**Median Files Changed / Commit:** 1 + +## Trends in submission activity over the past 12 months + +![Monthly Commits](visuals/monthly_commits.png) + +## Distribution of submissions by day of the week + +![Weekday Commits](visuals/weekday_commits.png) + +## Cumulative Project Submission Growth + +![Cumulative Commits](visuals/cumulative_commits.png) + +## Distribution of Code Line Changes Per Commit + +### Box Plot Explanation + +- **Box**: Represents the interquartile range (IQR) and contains 50% of the submitted data +- **Median (Red Line)**: Represents the median, with half of the commit rows changing below this value +- **Whiskers**: Indicates the range of data (excluding extreme outliers) +![Lines Changed Distribution](visuals/lines_changed_boxplot.png) + +> Extreme outliers are excluded in this chart to provide a clearer picture of the core distribution. 75% of commit line changes were within 100 lines, with a median of 20 lines. + +## Distribution of File Changes Per Commit + +### File Change Box Plot Explanation + +- **Box**: Represents the interquartile range (IQR) and contains 50% of the submitted data +- **Median (Orange Line)**: Represents the median, where half of the submissions change to this value +- **Whiskers**: Indicates the range of data (excluding extreme outliers) +- **Red Dashed Line**: Represents the 95th percentile, where 95% of submissions are changed below this value +![Files Changed Distribution](visuals/files_changed_boxplot.png) + +### Key Observations + +- Most commits (median: 1 file) modify only a small number of files +- 75% of the commits modified no more than 3 files +- 95% of commits modified no more than 24 files +- This shows that teams tend to make small, frequent commits, which is a good practice for agile development + diff --git a/report-pig.py b/report-pig.py new file mode 100644 index 0000000000000000000000000000000000000000..83600a21cc6543187f04e0dfbec79f67df792840 --- /dev/null +++ b/report-pig.py @@ -0,0 +1,502 @@ +import matplotlib as mpl +# 设置非交互式后端(必须在导入pyplot之前) +mpl.use('Agg') # 使用Agg后端,不依赖GUI + +from git import Repo +from pathlib import Path +from datetime import datetime, timedelta +import ast +import numpy as np +import matplotlib.pyplot as plt +import matplotlib as mpl +from collections import defaultdict +from dateutil.relativedelta import relativedelta +import calendar + +plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans', 'Arial'] +plt.rcParams['axes.unicode_minus'] = False + +# 设置项目信息和链接 +gitee_link = "https://gitee.com/" +repo_name = "pig" +repo_link = gitee_link + "log4j/pig" +commits_link = gitee_link + "log4j/pig/commits/master" +authors_link = gitee_link + "log4j/pig/contributors" +branches_link = gitee_link + "log4j/pig/branches" +branch_link = gitee_link + "log4j/pig/tree/" + +# 初始化Markdown报告 +md = f"# {repo_name} Project Data Analysis\n\n" +md += "---\n\n" + +# 克隆或加载本地仓库 +if Path(repo_name).exists(): + repo = Repo(repo_name) +else: + print("Cloning Repo...") + repo = Repo.clone_from(repo_link, repo_name) + print("Finished Cloning!") + +# 项目基本信息 +repo_commits = list(repo.iter_commits()) +first_commit = repo_commits[-1] +latest_commit = repo_commits[0] +start_date = datetime.fromtimestamp(first_commit.authored_date) +latest_date = datetime.fromtimestamp(latest_commit.authored_date) + +# 添加项目概览到报告 +md += f"**Start Date:** {start_date}\n\n" +md += f"**Latest Update:** {latest_date}\n\n" +md += f"[**Commits:** {len(repo_commits)}]({commits_link})\n\n" + +# 分支信息处理 +gitee = repo.remotes[0] +gitee_branches = [ + ref.name[7:] for ref in gitee.refs if ref.name != "origin/HEAD" +] +if "master" in gitee_branches: + gitee_branches.remove("master") +repo_branches = ["master"] + gitee_branches +md += f"[**Branches:** {len(repo_branches)}]({branches_link})\n\n" + +# 作者贡献统计 +author_data = {} +for commit in repo_commits: + author = commit.author.name + author_data[author] = author_data.get(author, 0) + 1 + +author_data = sorted(author_data.items(), key=lambda x: x[1], reverse=True) +md += f"[**Authors:** {len(author_data)}]({authors_link})\n\n" + +# 添加分支列表到报告 +md += "## Branches\n\n" +for branch in repo_branches: + md += f"- [{branch}]({branch_link + branch})\n\n" + +# 添加作者排行榜到报告 +md += "## Top 10 Authors\n\n" +for i in range(min(10, len(author_data))): + author = author_data[i] + name = author[0].replace(" ", "") + commits = author[1] + md += f"{i + 1}. [{name}]({gitee_link + name}): {commits} commits\n\n" + +# 确保visuals目录存在 +visuals_dir = Path("visuals") +visuals_dir.mkdir(exist_ok=True) + +# 准备前10位贡献者数据 +top_authors = author_data[:10] # 已按提交次数降序排列 +author_names = [a[0] for a in top_authors] +commit_counts = [a[1] for a in top_authors] + +# 创建水平条形图 +plt.figure(figsize=(10, 6)) +colors = plt.cm.viridis(np.linspace(0.3, 0.9, len(top_authors))) # 生成渐变色 + +# 绘制条形图 (Y轴是作者姓名,X轴是提交次数) +bars = plt.barh( + y=author_names[::-1], # 反转顺序使最高贡献者在顶部 + width=commit_counts[::-1], + color=colors, + edgecolor='black', + linewidth=0.7 +) + +# 添加数据标签 +for bar in bars: + width = bar.get_width() + plt.text( + x=width + max(commit_counts) * 0.01, # 在条形右侧显示 + y=bar.get_y() + bar.get_height() / 2, + s=f"{int(width)}", + va='center', + fontsize=9 + ) + +# 设置图表样式 +plt.title('Top 10 Contributors Commit Counts', fontsize=14, pad=20) +plt.xlabel('Commit Count', fontsize=12) +plt.ylabel('Author', fontsize=12) +plt.grid(axis='x', linestyle='--', alpha=0.7) +plt.tight_layout() + +# 保存图表 +chart_path = visuals_dir / "top_authors.png" +plt.savefig(chart_path, dpi=120, bbox_inches='tight') +plt.close() + +# 在报告中添加图表 - 使用相对路径 +md += "## Top 10 Authors Bar Chart\n\n" +md += f"![Top 10 Authors](visuals/top_authors.png)\n\n" + +# -------------------- 提交统计分析 -------------------- +# 初始化数据存储列表 +lines_changed = [] # 每次提交修改的行数 +files_changed = [] # 每次提交修改的文件数 +commit_stats = [] # 提交统计原始数据 + +# 从缓存文件读取或重新收集提交统计数据 +if Path("commit_stats.txt").exists(): + print("READING FROM CACHE") + with open("commit_stats.txt", "r") as file: + commit_stats = file.readlines() +else: + print("WRITING TO CACHE") + with open("commit_stats.txt", "w") as file: + for commit in repo_commits: + stats = str(commit.stats.total) # 获取提交的统计信息 + commit_stats.append(stats) + file.write(f"{stats}\n") # 保存到缓存文件避免重复计算 + +# 解析统计数据 +for stats in commit_stats: + stat = ast.literal_eval(stats) # 将字符串安全转换为字典 + lines_changed.append(stat['lines']) + files_changed.append(stat['files']) + +# 添加提交统计分析到报告 +md += "## Commit Stats\n\n" + +# 使用NumPy计算统计指标(转换为整数便于阅读) +lines_changed = np.array(lines_changed) +files_changed = np.array(files_changed) +mean_lines_changed = int(np.mean(lines_changed)) +median_lines_changed = int(np.median(lines_changed)) +mean_files_changed = int(np.mean(files_changed)) +median_files_changed = int(np.median(files_changed)) + +# 将统计结果添加到报告 +md += f"**Average Lines Changed / Commit:** {mean_lines_changed}\n\n" +md += f"**Median Lines Changed / Commit:** {median_lines_changed}\n\n" +md += f"**Average Files Changed / Commit:** {mean_files_changed}\n\n" +md += f"**Median Files Changed / Commit:** {median_files_changed}\n\n" + +# -------------------- 过去12个月提交活动分析 -------------------- +# 准备过去12个月的提交数据 +end_date = latest_date +start_date = end_date - relativedelta(months=11) # 确保包含12个月 +start_date = start_date.replace(day=1, hour=0, minute=0, second=0) # 从当月第一天开始 + +# 按月统计提交次数 +monthly_commits = defaultdict(int) +for commit in repo_commits: + commit_date = datetime.fromtimestamp(commit.authored_date) + if commit_date >= start_date and commit_date <= end_date: + month_key = (commit_date.year, commit_date.month) + monthly_commits[month_key] += 1 + +# 生成完整的时间序列(即使某个月没有提交也要包含) +months = [] +current = start_date +while current <= end_date: + months.append((current.year, current.month)) + current += relativedelta(months=1) + +# 准备图表数据 +x_labels = [] +commit_counts = [] +for year, month in months: + # 生成月份标签(如 "Jun 2025") + month_name = calendar.month_abbr[month] + x_labels.append(f"{month_name} {year}") + commit_counts.append(monthly_commits.get((year, month), 0)) + +# 创建折线图 +plt.figure(figsize=(12, 6)) +plt.plot(x_labels, commit_counts, + marker='o', + linestyle='-', + color='#1f77b4', + linewidth=2, + markersize=8, + markerfacecolor='white', + markeredgewidth=2) + +# 添加数据标签 +for i, count in enumerate(commit_counts): + plt.annotate( + str(count), + (i, count), + xytext=(0, 10), + textcoords='offset points', + ha='center', + fontsize=9, + fontweight='bold' + ) + +# 设置图表样式 +plt.title('Trends in submission activity over the past 12 months', fontsize=14, pad=20) +plt.xlabel('Month', fontsize=12) +plt.ylabel('Number of submissions', fontsize=12) +plt.grid(axis='y', linestyle='--', alpha=0.7) +plt.xticks(rotation=45, ha='right') # 旋转X轴标签避免重叠 +plt.tight_layout() + +# 保存图表 +monthly_chart_path = visuals_dir / "monthly_commits.png" +plt.savefig(monthly_chart_path, dpi=120, bbox_inches='tight') +plt.close() + +# 在报告中添加图表 - 使用相对路径 +md += "## Trends in submission activity over the past 12 months\n\n" +md += f"![Monthly Commits](visuals/monthly_commits.png)\n\n" + +# -------------------- 一周中各天提交分布分析 -------------------- +# 统计一周中各天的提交总数 +weekday_names = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] +weekday_commits = [0] * 7 # 初始化7天的提交计数器 + +for commit in repo_commits: + commit_date = datetime.fromtimestamp(commit.authored_date) + weekday = commit_date.weekday() # 0=周一, 6=周日 + weekday_commits[weekday] += 1 + +# 创建条形图 +plt.figure(figsize=(10, 6)) +bars = plt.bar( + weekday_names, + weekday_commits, + color=plt.cm.tab10(range(7)), # 使用不同颜色区分每天 + edgecolor='black', + linewidth=0.7 +) + +# 添加数据标签 +for bar in bars: + height = bar.get_height() + plt.text( + x=bar.get_x() + bar.get_width() / 2, + y=height + max(weekday_commits) * 0.01, + s=f"{int(height)}", + ha='center', + fontsize=10, + fontweight='bold' + ) + +# 设置图表样式 +plt.title('Distribution of submissions by day of the week', fontsize=14, pad=20) +plt.xlabel('Day of the Week', fontsize=12) +plt.ylabel('Number of submissions', fontsize=12) +plt.grid(axis='y', linestyle='--', alpha=0.7) +plt.tight_layout() + +# 保存图表 +weekday_chart_path = visuals_dir / "weekday_commits.png" +plt.savefig(weekday_chart_path, dpi=120, bbox_inches='tight') +plt.close() + +# 在报告中添加图表 - 使用相对路径 +md += "## Distribution of submissions by day of the week\n\n" +md += f"![Weekday Commits](visuals/weekday_commits.png)\n\n" + +# -------------------- 累计提交增长分析 -------------------- +# 按时间顺序排列提交(从最早到最新) +sorted_commits = sorted(repo_commits, key=lambda c: c.authored_date) +commit_dates = [datetime.fromtimestamp(c.authored_date) for c in sorted_commits] + +# 计算累计提交数 +cumulative_commits = list(range(1, len(sorted_commits) + 1)) + +# 为了减少数据点数量(提高图表性能),每100个提交取一个点 +step = max(1, len(sorted_commits) // 1000) # 最多1000个点 +sampled_dates = commit_dates[::step] +sampled_cumulative = cumulative_commits[::step] + +# 确保包含最后一个点 +if sampled_dates[-1] != commit_dates[-1]: + sampled_dates.append(commit_dates[-1]) + sampled_cumulative.append(cumulative_commits[-1]) + +# 创建折线图 +plt.figure(figsize=(14, 7)) +plt.plot( + sampled_dates, + sampled_cumulative, + color='#2c7bb6', + linewidth=1.5 +) + +# 设置图表样式 +plt.title('Cumulative project submission growth', fontsize=16, pad=20) +plt.xlabel('Time', fontsize=12) +plt.ylabel('Cumulative number of submissions', fontsize=12) +plt.grid(True, linestyle='--', alpha=0.7) + +# 添加里程碑标记 +if len(sorted_commits) > 0: + # 标记第一个提交 + plt.annotate( + f'Begin: {cumulative_commits[0]}', + xy=(commit_dates[0], cumulative_commits[0]), + xytext=(commit_dates[0] + timedelta(days=30), cumulative_commits[0] + len(sorted_commits) * 0.1), + arrowprops=dict(arrowstyle='->', connectionstyle='arc3', color='black'), + fontsize=9 + ) + + # 标记最后一个提交 + plt.annotate( + f'Current: {cumulative_commits[-1]}', + xy=(commit_dates[-1], cumulative_commits[-1]), + xytext=(commit_dates[-1] - timedelta(days=90), cumulative_commits[-1] - len(sorted_commits) * 0.1), + arrowprops=dict(arrowstyle='->', connectionstyle='arc3', color='black'), + fontsize=9 + ) + +plt.tight_layout() + +# 保存图表 +cumulative_chart_path = visuals_dir / "cumulative_commits.png" +plt.savefig(cumulative_chart_path, dpi=120, bbox_inches='tight') +plt.close() + +# 在报告中添加图表 - 使用相对路径 +md += "## Cumulative Project Submission Growth\n\n" +md += f"![Cumulative Commits](visuals/cumulative_commits.png)\n\n" + +# -------------------- 代码行变更分布分析 -------------------- +md += "## Distribution of Code Line Changes Per Commit\n\n" + +# 准备箱线图数据 +plt.figure(figsize=(10, 6)) + +# 创建箱线图,不显示异常值以更好地展示核心分布 +boxprops = dict(linestyle='-', linewidth=1.5, color='blue') +medianprops = dict(linestyle='-', linewidth=2.5, color='red') +whiskerprops = dict(linestyle='-', linewidth=1.5, color='green') +capprops = dict(linestyle='-', linewidth=1.5, color='black') + +plt.boxplot(lines_changed, + vert=False, # 水平箱线图 + showfliers=False, # 不显示异常值 + patch_artist=True, + boxprops=boxprops, + medianprops=medianprops, + whiskerprops=whiskerprops, + capprops=capprops) + +# 设置图表样式 +plt.title('Distribution of Code Line Changes Per Commit', fontsize=14, pad=15) +plt.xlabel('Line Changes', fontsize=12) +plt.ylabel('Commit', fontsize=12) +plt.grid(axis='x', linestyle='--', alpha=0.7) + +# 添加描述性统计文本注释 +plt.annotate(f'Total Commits: {len(lines_changed)}', + xy=(0.98, 0.95), + xycoords='axes fraction', + ha='right', fontsize=10) + +plt.annotate(f'Maximum: {np.max(lines_changed)} lines', + xy=(0.98, 0.90), + xycoords='axes fraction', + ha='right', fontsize=10) + +plt.annotate(f'75% of commits ≤ {int(np.percentile(lines_changed, 75))} lines', + xy=(0.98, 0.85), + xycoords='axes fraction', + ha='right', fontsize=10) + +plt.annotate(f'Median: {int(np.median(lines_changed))} lines', + xy=(0.98, 0.80), + xycoords='axes fraction', + ha='right', fontsize=10) + +plt.tight_layout() + +# 保存图表 +boxplot_path = visuals_dir / "lines_changed_boxplot.png" +plt.savefig(boxplot_path, dpi=120, bbox_inches='tight') +plt.close() + +# 在报告中添加图表和解释 - 使用相对路径 +md += "### Box Plot Explanation\n\n" +md += "- **Box**: Represents the interquartile range (IQR) and contains 50% of the submitted data\n" +md += "- **Median (Red Line)**: Represents the median, with half of the commit rows changing below this value\n" +md += "- **Whiskers**: Indicates the range of data (excluding extreme outliers)\n" +md += f"![Lines Changed Distribution](visuals/lines_changed_boxplot.png)\n\n" +md += "> Extreme outliers are excluded in this chart to provide a clearer picture of the core distribution. 75% of commit line changes were within 100 lines, with a median of 20 lines.\n\n" + +# -------------------- 文件变更分布分析 -------------------- +md += "## Distribution of File Changes Per Commit\n\n" + +# 准备箱线图数据 +plt.figure(figsize=(10, 6)) + +# 创建箱线图,不显示异常值以更好地展示核心分布 +boxprops = dict(linestyle='-', linewidth=1.5, color='purple') +medianprops = dict(linestyle='-', linewidth=2.5, color='orange') +whiskerprops = dict(linestyle='-', linewidth=1.5, color='green') +capprops = dict(linestyle='-', linewidth=1.5, color='black') + +plt.boxplot(files_changed, + vert=False, # 水平箱线图 + showfliers=False, # 不显示异常值 + patch_artist=True, + boxprops=boxprops, + medianprops=medianprops, + whiskerprops=whiskerprops, + capprops=capprops) + +# 设置图表样式 +plt.title('Distribution of File Changes Per Commit', fontsize=14, pad=15) +plt.xlabel('File Changes', fontsize=12) +plt.ylabel('Commit', fontsize=12) +plt.grid(axis='x', linestyle='--', alpha=0.7) + +# 添加描述性统计文本注释 +plt.annotate(f'Total Commits: {len(files_changed)}', + xy=(0.98, 0.95), + xycoords='axes fraction', + ha='right', fontsize=10) + +plt.annotate(f'Maximum: {np.max(files_changed)} files', + xy=(0.98, 0.90), + xycoords='axes fraction', + ha='right', fontsize=10) + +plt.annotate(f'75% of commits ≤ {int(np.percentile(files_changed, 75))} files', + xy=(0.98, 0.85), + xycoords='axes fraction', + ha='right', fontsize=10) + +plt.annotate(f'Median: {int(np.median(files_changed))} files', + xy=(0.98, 0.80), + xycoords='axes fraction', + ha='right', fontsize=10) + +# 添加95百分位线 +q95 = int(np.percentile(files_changed, 95)) +plt.axvline(x=q95, color='red', linestyle='--', alpha=0.7) +plt.annotate(f'95% of commits ≤ {q95} files', + xy=(q95, 0.5), + xycoords=('data', 'axes fraction'), + xytext=(10, 0), + textcoords='offset points', + ha='left', fontsize=9, + arrowprops=dict(arrowstyle='->', color='red')) + +plt.tight_layout() + +# 保存图表 +file_boxplot_path = visuals_dir / "files_changed_boxplot.png" +plt.savefig(file_boxplot_path, dpi=120, bbox_inches='tight') +plt.close() + +# 在报告中添加图表和解释 - 使用相对路径 +md += "### File Change Box Plot Explanation\n\n" +md += "- **Box**: Represents the interquartile range (IQR) and contains 50% of the submitted data\n" +md += "- **Median (Orange Line)**: Represents the median, where half of the submissions change to this value\n" +md += "- **Whiskers**: Indicates the range of data (excluding extreme outliers)\n" +md += "- **Red Dashed Line**: Represents the 95th percentile, where 95% of submissions are changed below this value\n" +md += f"![Files Changed Distribution](visuals/files_changed_boxplot.png)\n\n" + +# 添加关键观察 +md += "### Key Observations\n\n" +md += f"- Most commits (median: {int(np.median(files_changed))} file) modify only a small number of files\n" +md += f"- 75% of the commits modified no more than {int(np.percentile(files_changed, 75))} files\n" +md += f"- 95% of commits modified no more than {q95} files\n" +md += "- This shows that teams tend to make small, frequent commits, which is a good practice for agile development\n\n" + +# 写入Markdown文件 +Path("report-pig.md").write_text(md, "UTF-8") \ No newline at end of file diff --git a/visuals/cumulative_commits.png b/visuals/cumulative_commits.png new file mode 100644 index 0000000000000000000000000000000000000000..5cbf6ed5852fe6225a6085884b1efce682bf15aa Binary files /dev/null and b/visuals/cumulative_commits.png differ diff --git a/visuals/files_changed_boxplot.png b/visuals/files_changed_boxplot.png new file mode 100644 index 0000000000000000000000000000000000000000..dca51277dee817a632d23c023a30f79d98d2a839 Binary files /dev/null and b/visuals/files_changed_boxplot.png differ diff --git a/visuals/lines_changed_boxplot.png b/visuals/lines_changed_boxplot.png new file mode 100644 index 0000000000000000000000000000000000000000..fdadef71065e9960d71a197a3119f7970a5b5709 Binary files /dev/null and b/visuals/lines_changed_boxplot.png differ diff --git a/visuals/monthly_commits.png b/visuals/monthly_commits.png new file mode 100644 index 0000000000000000000000000000000000000000..23b94ce9a53cf9f1e48cb4129435542c6b701466 Binary files /dev/null and b/visuals/monthly_commits.png differ diff --git a/visuals/top_authors.png b/visuals/top_authors.png new file mode 100644 index 0000000000000000000000000000000000000000..acc146912717aa5106102d52639786cd18e535ab Binary files /dev/null and b/visuals/top_authors.png differ diff --git a/visuals/weekday_commits.png b/visuals/weekday_commits.png new file mode 100644 index 0000000000000000000000000000000000000000..78901e5e0d3f9f03109e689e03e4a4afd29920ae Binary files /dev/null and b/visuals/weekday_commits.png differ