Rewarding main contributors

Hello pyReviters,

I mentioned, a month back or so, I was willing to reward contribution to the repo and community with the money collected from supporters on Open Collective pyRevitLabs · Expenses - Open Collective.

I thought first about making a bounty program, but rapidly felt this would be overwhelming. I don’t have that much time to put into it, and the management time would have been disproportionate. So like every good autocrat and coder, I made a script to harvest relevant data from the github repo (cursor and a few minutes later - see at the end of this post if you are curious)

So here they are the proud contributors, reviewers, actors, action figures of the past months in no particular order:

All of them received a 250 € or $ gift card from whatever eshop they liked or used (or the ones I could get to process my payment methods from Czech Rep. :grimacing: )

Notes:
_ I know € or $ are not equal _ don’t bother, I told you I am the autocrat
_ I also know that not all contributions are equal in terms of the value, time invested, … but to me, getting to spend your own time to improve pyRevit for an extended period is something valuable in itself, and not everyone comes with the same background, so, All volunteers are welcome if they help sustain pyRevit _ you can add socialist/communist to autocrat :winking_face_with_tongue:
_ If you want to take a look at how the money is being used, this is all there pyRevitLabs · Expenses - Open Collective

GitHub stats extractor
# GitHub Issues and PRs Tracker for pyRevit
# Compatible with IronPython 2.7.12 and Python 3.x

# For IronPython 2.7.12, use urllib2
# For Python 3.x, use urllib.request
try:
    import urllib2
    import json
    import os

    PYTHON_VERSION = 2
except ImportError:
    import urllib.request as urllib2
    import json
    import os

    PYTHON_VERSION = 3

from datetime import datetime, timedelta
from collections import defaultdict
import time


# Load environment variables from .env file
def load_env_file():
    """Load environment variables from .env file"""
    try:
        env_file = ".env"
        if os.path.exists(env_file):
            with open(env_file, "r") as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith("#") and "=" in line:
                        key, value = line.split("=", 1)
                        os.environ[key.strip()] = value.strip()
    except Exception as e:
        print("Warning: Could not load .env file: " + str(e))


# Load environment variables
load_env_file()


def get_github_data(url, retry_count=0):
    """Make a request to GitHub API and return JSON data with rate limiting"""
    try:
        request = urllib2.Request(url)
        request.add_header("Accept", "application/vnd.github.v3+json")
        request.add_header("User-Agent", "pyRevit-issue-tracker")

        # Add authentication header if token is available
        github_token = os.environ.get("GITHUBTOKEN")
        if github_token:
            request.add_header("Authorization", "Bearer " + github_token)

        response = urllib2.urlopen(request)
        if PYTHON_VERSION == 3:
            data = json.loads(response.read().decode("utf-8"))
        else:
            data = json.loads(response.read())

        # Add a small delay to respect rate limits
        time.sleep(0.1)
        return data

    except urllib2.HTTPError as e:
        if e.code == 429 or e.code == 403:  # Rate limit exceeded
            if retry_count < 3:
                wait_time = (retry_count + 1) * 5  # Wait 5, 10, 15 seconds
                print("Rate limit exceeded. Waiting " + str(wait_time) + " seconds...")
                time.sleep(wait_time)
                return get_github_data(url, retry_count + 1)
            else:
                print("Rate limit exceeded. Please try again later.")
                return None
        else:
            print("HTTP Error " + str(e.code) + ": " + str(e))
            return None
    except Exception as e:
        print("Error fetching data: " + str(e))
        return None


def get_closed_issues_for_month(year, month):
    """Get all closed issues for a specific month"""
    start_date = "{:04d}-{:02d}-01".format(year, month)

    if month == 12:
        next_month = 1
        next_year = year + 1
    else:
        next_month = month + 1
        next_year = year

    next_month_first = datetime(next_year, next_month, 1)
    last_day = (next_month_first - timedelta(days=1)).day
    end_date = "{:04d}-{:02d}-{:02d}".format(year, month, last_day)

    search_url = (
        "https://api.github.com/search/issues?q=repo:pyrevitlabs/pyRevit+is:issue+is:closed+closed:"
        + start_date
        + ".."
        + end_date
        + "&per_page=100"
    )

    data = get_github_data(search_url)
    return data.get("items", []) if data else []


def get_merged_prs_for_month(year, month):
    """Get all merged PRs for a specific month"""
    start_date = "{:04d}-{:02d}-01".format(year, month)

    if month == 12:
        next_month = 1
        next_year = year + 1
    else:
        next_month = month + 1
        next_year = year

    next_month_first = datetime(next_year, next_month, 1)
    last_day = (next_month_first - timedelta(days=1)).day
    end_date = "{:04d}-{:02d}-{:02d}".format(year, month, last_day)

    search_url = (
        "https://api.github.com/search/issues?q=repo:pyrevitlabs/pyRevit+is:pr+is:merged+merged:"
        + start_date
        + ".."
        + end_date
        + "&per_page=100"
    )

    data = get_github_data(search_url)
    return data.get("items", []) if data else []


def get_issue_closer(issue_number):
    """Get who closed a specific issue by checking events"""
    events_url = (
        "https://api.github.com/repos/pyrevitlabs/pyRevit/issues/"
        + str(issue_number)
        + "/events?per_page=100"
    )

    data = get_github_data(events_url)
    if not data:
        return None

    for event in data:
        if event.get("event") == "closed":
            actor = event.get("actor")
            if actor:
                return actor.get("login")
    return None


def get_pr_merger(pr_number):
    """Get who merged a specific PR by checking events"""
    events_url = (
        "https://api.github.com/repos/pyrevitlabs/pyRevit/issues/"
        + str(pr_number)
        + "/events?per_page=100"
    )

    data = get_github_data(events_url)
    if not data:
        return None

    for event in data:
        if event.get("event") == "merged":
            actor = event.get("actor")
            if actor:
                return actor.get("login")
    return None


def get_pr_comments(pr_number):
    """Get all comments for a specific PR and count by user"""
    comments_url = (
        "https://api.github.com/repos/pyrevitlabs/pyRevit/issues/"
        + str(pr_number)
        + "/comments?per_page=100"
    )

    data = get_github_data(comments_url)
    if not data:
        return {}

    comment_counts = defaultdict(int)
    for comment in data:
        user = comment.get("user", {}).get("login")
        if user:
            comment_counts[user] += 1

    return comment_counts


def process_month(year, month, query_type="issues"):
    """Process a single month and return results"""
    month_names = [
        "",
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    ]
    month_name = month_names[month]

    results = {}

    if query_type in ["issues", "both"]:
        closed_issues = get_closed_issues_for_month(year, month)
        if closed_issues:
            closer_counts = defaultdict(int)
            for issue in closed_issues:
                closer = get_issue_closer(issue.get("number"))
                if closer:
                    closer_counts[closer] += 1
            results["issues"] = (closer_counts, len(closed_issues))

    if query_type in ["prs", "both"]:
        merged_prs = get_merged_prs_for_month(year, month)
        if merged_prs:
            merger_counts = defaultdict(int)
            creator_counts = defaultdict(int)
            comment_counts = defaultdict(int)
            for pr in merged_prs:
                # Track who merged the PR
                merger = get_pr_merger(pr.get("number"))
                if merger:
                    merger_counts[merger] += 1

                # Track who created the PR
                creator = pr.get("user", {}).get("login")
                if creator:
                    creator_counts[creator] += 1

                # Track comments on the PR
                pr_comments = get_pr_comments(pr.get("number"))
                for user, count in pr_comments.items():
                    comment_counts[user] += count

            results["prs"] = (merger_counts, len(merged_prs))
            results["pr_creators"] = (creator_counts, len(merged_prs))
            results["pr_comments"] = (comment_counts, len(merged_prs))

    return results


def process_year(year, query_type="issues"):
    """Process all months of a year and generate report"""
    print("Processing " + str(year) + " (" + query_type + ")...")

    month_names = [
        "",
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    ]

    yearly_issue_counts = defaultdict(int)
    yearly_pr_counts = defaultdict(int)
    yearly_pr_creator_counts = defaultdict(int)
    yearly_pr_comment_counts = defaultdict(int)
    yearly_total_issues = 0
    yearly_total_prs = 0
    monthly_results = []

    for month in range(1, 13):
        month_name = month_names[month]
        print("  " + month_name + "...", end=" ")

        results = process_month(year, month, query_type)

        if results:
            monthly_results.append((month_name, results))

            if "issues" in results:
                closer_counts, total_issues = results["issues"]
                yearly_total_issues += total_issues
                for closer, count in closer_counts.items():
                    yearly_issue_counts[closer] += count

            if "prs" in results:
                merger_counts, total_prs = results["prs"]
                yearly_total_prs += total_prs
                for merger, count in merger_counts.items():
                    yearly_pr_counts[merger] += count

            if "pr_creators" in results:
                creator_counts, total_prs = results["pr_creators"]
                for creator, count in creator_counts.items():
                    yearly_pr_creator_counts[creator] += count

            if "pr_comments" in results:
                comment_counts, total_prs = results["pr_comments"]
                for user, count in comment_counts.items():
                    yearly_pr_comment_counts[user] += count

        print("✓")
        if month < 12:
            time.sleep(0.2)

    # Generate report
    report_lines = []
    report_lines.append("=== pyRevit GitHub Activity Report - " + str(year) + " ===")
    report_lines.append("Repository: pyrevitlabs/pyRevit")
    report_lines.append(
        "Generated: " + str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    )
    report_lines.append("")

    if query_type in ["issues", "both"]:
        report_lines.append("=== CLOSED ISSUES ===")
        report_lines.append("-" * 50)

        for month_name, results in monthly_results:
            if "issues" in results:
                closer_counts, total_issues = results["issues"]
                if total_issues > 0:
                    report_lines.append(
                        month_name
                        + " "
                        + str(year)
                        + ": "
                        + str(total_issues)
                        + " issues"
                    )
                    sorted_closers = sorted(
                        closer_counts.items(), key=lambda x: x[1], reverse=True
                    )
                    for closer, count in sorted_closers:
                        report_lines.append(
                            "  " + closer + ": " + str(count) + " issues"
                        )
                    report_lines.append("")

        report_lines.append(
            "Total issues closed in " + str(year) + ": " + str(yearly_total_issues)
        )
        report_lines.append("")

        if yearly_issue_counts:
            report_lines.append("Issues closed by each person (yearly total):")
            report_lines.append("-" * 50)
            sorted_closers = sorted(
                yearly_issue_counts.items(), key=lambda x: x[1], reverse=True
            )
            for closer, count in sorted_closers:
                report_lines.append(closer + ": " + str(count) + " issues")
        report_lines.append("")

    if query_type in ["prs", "both"]:
        report_lines.append("=== MERGED PULL REQUESTS ===")
        report_lines.append("-" * 50)

        for month_name, results in monthly_results:
            if "prs" in results:
                merger_counts, total_prs = results["prs"]
                if total_prs > 0:
                    report_lines.append(
                        month_name + " " + str(year) + ": " + str(total_prs) + " PRs"
                    )

                    # Show who merged PRs
                    sorted_mergers = sorted(
                        merger_counts.items(), key=lambda x: x[1], reverse=True
                    )
                    for merger, count in sorted_mergers:
                        report_lines.append(
                            "  Merged by " + merger + ": " + str(count) + " PRs"
                        )

                    # Show who created PRs
                    if "pr_creators" in results:
                        creator_counts, _ = results["pr_creators"]
                        sorted_creators = sorted(
                            creator_counts.items(), key=lambda x: x[1], reverse=True
                        )
                        for creator, count in sorted_creators:
                            report_lines.append(
                                "  Created by " + creator + ": " + str(count) + " PRs"
                            )

                    # Show comment activity
                    if "pr_comments" in results:
                        comment_counts, _ = results["pr_comments"]
                        if comment_counts:
                            sorted_commenters = sorted(
                                comment_counts.items(), key=lambda x: x[1], reverse=True
                            )
                            for commenter, count in sorted_commenters:
                                report_lines.append(
                                    "  Comments by "
                                    + commenter
                                    + ": "
                                    + str(count)
                                    + " comments"
                                )

                    report_lines.append("")

        report_lines.append(
            "Total PRs merged in " + str(year) + ": " + str(yearly_total_prs)
        )
        report_lines.append("")

        if yearly_pr_counts:
            report_lines.append("PRs merged by each person (yearly total):")
            report_lines.append("-" * 50)
            sorted_mergers = sorted(
                yearly_pr_counts.items(), key=lambda x: x[1], reverse=True
            )
            for merger, count in sorted_mergers:
                report_lines.append(merger + ": " + str(count) + " PRs")
            report_lines.append("")

        if yearly_pr_creator_counts:
            report_lines.append("PRs created by each person (yearly total):")
            report_lines.append("-" * 50)
            sorted_creators = sorted(
                yearly_pr_creator_counts.items(), key=lambda x: x[1], reverse=True
            )
            for creator, count in sorted_creators:
                report_lines.append(creator + ": " + str(count) + " PRs")
            report_lines.append("")

        if yearly_pr_comment_counts:
            report_lines.append("PR comments by each person (yearly total):")
            report_lines.append("-" * 50)
            sorted_commenters = sorted(
                yearly_pr_comment_counts.items(), key=lambda x: x[1], reverse=True
            )
            for commenter, count in sorted_commenters:
                report_lines.append(commenter + ": " + str(count) + " comments")
        report_lines.append("")

    # Save to file
    filename = "pyrevit_report_" + str(year) + "_" + query_type + ".txt"
    with open(filename, "w") as f:
        for line in report_lines:
            f.write(line + "\n")

    # Print summary
    print("\n=== SUMMARY ===")
    for line in report_lines:
        print(line)

    print("\nReport saved to: " + filename)

    # Open file
    try:
        if os.name == "nt":  # Windows
            os.startfile(filename)
        elif os.name == "posix":  # macOS and Linux
            os.system('open "' + filename + '"')
    except:
        print(
            "Could not automatically open the file. Please open "
            + filename
            + " manually."
        )


def main():
    import sys

    # Check authentication
    github_token = os.environ.get("GITHUBTOKEN")
    if github_token:
        print("✓ GitHub token found - using authenticated requests")
    else:
        print("⚠ No GitHub token found - using unauthenticated requests")

    # Parse command line arguments
    if len(sys.argv) >= 4:
        year = int(sys.argv[1])
        query_type = sys.argv[2].lower()
        if sys.argv[3].lower() == "all":
            process_year(year, query_type)
        else:
            month = int(sys.argv[3])
            results = process_month(year, month, query_type)
            print(
                "Results for " + str(month) + "/" + str(year) + " (" + query_type + "):"
            )
            for key, (counts, total) in results.items():
                if key == "prs":
                    print("PRS MERGED: " + str(total))
                    for user, count in sorted(
                        counts.items(), key=lambda x: x[1], reverse=True
                    ):
                        print("  Merged by " + user + ": " + str(count))
                elif key == "pr_creators":
                    print("PRS CREATED: " + str(total))
                    for user, count in sorted(
                        counts.items(), key=lambda x: x[1], reverse=True
                    ):
                        print("  Created by " + user + ": " + str(count))
                elif key == "pr_comments":
                    print("PR COMMENTS: " + str(total))
                    for user, count in sorted(
                        counts.items(), key=lambda x: x[1], reverse=True
                    ):
                        print("  Comments by " + user + ": " + str(count))
                else:
                    print(key.upper() + ": " + str(total))
                    for user, count in sorted(
                        counts.items(), key=lambda x: x[1], reverse=True
                    ):
                        print("  " + user + ": " + str(count))
    elif len(sys.argv) >= 3:
        year = int(sys.argv[1])
        if sys.argv[2].lower() == "all":
            process_year(year, "issues")
        else:
            month = int(sys.argv[2])
            results = process_month(year, month, "issues")
            print("Results for " + str(month) + "/" + str(year) + " (issues):")
            for key, (counts, total) in results.items():
                if key == "prs":
                    print("PRS MERGED: " + str(total))
                    for user, count in sorted(
                        counts.items(), key=lambda x: x[1], reverse=True
                    ):
                        print("  Merged by " + user + ": " + str(count))
                elif key == "pr_creators":
                    print("PRS CREATED: " + str(total))
                    for user, count in sorted(
                        counts.items(), key=lambda x: x[1], reverse=True
                    ):
                        print("  Created by " + user + ": " + str(count))
                elif key == "pr_comments":
                    print("PR COMMENTS: " + str(total))
                    for user, count in sorted(
                        counts.items(), key=lambda x: x[1], reverse=True
                    ):
                        print("  Comments by " + user + ": " + str(count))
                else:
                    print(key.upper() + ": " + str(total))
                    for user, count in sorted(
                        counts.items(), key=lambda x: x[1], reverse=True
                    ):
                        print("  " + user + ": " + str(count))
    else:
        print("Usage:")
        print("  python script_new.py YEAR TYPE MONTH     # Single month")
        print("  python script_new.py YEAR TYPE all       # All months")
        print("  python script_new.py YEAR MONTH          # Single month (issues only)")
        print("  python script_new.py YEAR all            # All months (issues only)")
        print()
        print("Types: issues, prs, both")
        print("Examples:")
        print("  python script_new.py 2024 issues 8       # August 2024 issues")
        print("  python script_new.py 2024 prs all        # All 2024 PRs")
        print("  python script_new.py 2024 both all       # All 2024 issues and PRs")


if __name__ == "__main__":
    main()

Results for 2025

=== pyRevit GitHub Activity Report - 2025 ===
Repository: pyrevitlabs/pyRevit
Generated: 2025-09-18 14:37:06

=== CLOSED ISSUES ===

January 2025: 35 issues
jmcouffin: 24 issues
sanzoghenzo: 5 issues
belphegore1: 1 issues
dosymep: 1 issues
sergeykahn: 1 issues
archeduardo5: 1 issues
svalenziano: 1 issues
github-actions[bot]: 1 issues

February 2025: 17 issues
jmcouffin: 9 issues
sanzoghenzo: 3 issues
wxWANG0128: 1 issues
CMIller3906: 1 issues
szcsaba87: 1 issues
baaswietse: 1 issues
agialanella: 1 issues

March 2025: 10 issues
jmcouffin: 8 issues
rbunns: 1 issues
zsenarchitect: 1 issues

April 2025: 8 issues
jmcouffin: 6 issues
KonradZaremba: 1 issues
github-actions[bot]: 1 issues

May 2025: 8 issues
jmcouffin: 7 issues
sanzoghenzo: 1 issues

June 2025: 11 issues
jmcouffin: 7 issues
ScottMycoMech: 1 issues
sanzoghenzo: 1 issues
Wanderson97-ss: 1 issues
olalancette: 1 issues

July 2025: 4 issues
jmcouffin: 2 issues
arkitek2s: 1 issues
JCurvey: 1 issues

August 2025: 26 issues
jmcouffin: 21 issues
tay0thman: 3 issues
Wurschdhaud: 1 issues
MohamedAsli: 1 issues

September 2025: 16 issues
jmcouffin: 7 issues
MohamedAsli: 7 issues
Emmawadlow: 1 issues
RichardPinka: 1 issues

Total issues closed in 2025: 135

Issues closed by each person (yearly total):

jmcouffin: 91 issues
sanzoghenzo: 10 issues
MohamedAsli: 8 issues
tay0thman: 3 issues
github-actions[bot]: 2 issues
belphegore1: 1 issues
dosymep: 1 issues
sergeykahn: 1 issues
archeduardo5: 1 issues
svalenziano: 1 issues
wxWANG0128: 1 issues
CMIller3906: 1 issues
szcsaba87: 1 issues
baaswietse: 1 issues
agialanella: 1 issues
rbunns: 1 issues
zsenarchitect: 1 issues
KonradZaremba: 1 issues
ScottMycoMech: 1 issues
Wanderson97-ss: 1 issues
olalancette: 1 issues
arkitek2s: 1 issues
JCurvey: 1 issues
Wurschdhaud: 1 issues
Emmawadlow: 1 issues
RichardPinka: 1 issues

=== MERGED PULL REQUESTS ===

January 2025: 25 PRs
Merged by jmcouffin: 23 PRs
Merged by sanzoghenzo: 2 PRs
Created by jmcouffin: 11 PRs
Created by sanzoghenzo: 7 PRs
Created by thumDer: 2 PRs
Created by dependabot[bot]: 2 PRs
Created by devloai[bot]: 1 PRs
Created by reformstudios: 1 PRs
Created by Malouche-git: 1 PRs
Comments by github-actions[bot]: 440 comments
Comments by jmcouffin: 57 comments
Comments by sanzoghenzo: 25 comments
Comments by devloai[bot]: 6 comments
Comments by dosymep: 2 comments
Comments by thumDer: 1 comments
Comments by Malouche-git: 1 comments

February 2025: 24 PRs
Merged by jmcouffin: 21 PRs
Merged by sanzoghenzo: 3 PRs
Created by jmcouffin: 5 PRs
Created by dependabot[bot]: 5 PRs
Created by nasmovk: 3 PRs
Created by thumDer: 2 PRs
Created by sanzoghenzo: 2 PRs
Created by Malouche-git: 2 PRs
Created by tay0thman: 1 PRs
Created by mangrove-art: 1 PRs
Created by devloai[bot]: 1 PRs
Created by jorisvandermeulen: 1 PRs
Created by trgiangv: 1 PRs
Comments by github-actions[bot]: 142 comments
Comments by jmcouffin: 26 comments
Comments by devloai[bot]: 9 comments
Comments by sanzoghenzo: 7 comments
Comments by nasmovk: 6 comments
Comments by Malouche-git: 6 comments
Comments by thumDer: 2 comments
Comments by jorisvandermeulen: 2 comments
Comments by trgiangv: 2 comments

March 2025: 12 PRs
Merged by jmcouffin: 12 PRs
Created by dependabot[bot]: 5 PRs
Created by jmcouffin: 3 PRs
Created by Wurschdhaud: 2 PRs
Created by jonatanjacobsson: 1 PRs
Created by 924312: 1 PRs
Comments by github-actions[bot]: 171 comments
Comments by jmcouffin: 2 comments

April 2025: 25 PRs
Merged by jmcouffin: 25 PRs
Created by jmcouffin: 7 PRs
Created by dependabot[bot]: 6 PRs
Created by tay0thman: 5 PRs
Created by Wurschdhaud: 1 PRs
Created by devloai[bot]: 1 PRs
Created by jonatanjacobsson: 1 PRs
Created by nodatasheet: 1 PRs
Created by leyarx: 1 PRs
Created by iorhanV: 1 PRs
Created by dnenov: 1 PRs
Comments by github-actions[bot]: 524 comments
Comments by jmcouffin: 23 comments
Comments by devloai[bot]: 9 comments
Comments by tay0thman: 6 comments
Comments by dnenov: 2 comments
Comments by nodatasheet: 1 comments
Comments by sanzoghenzo: 1 comments

May 2025: 15 PRs
Merged by jmcouffin: 15 PRs
Created by dependabot[bot]: 5 PRs
Created by jmcouffin: 4 PRs
Created by tay0thman: 3 PRs
Created by Wurschdhaud: 1 PRs
Created by 924312: 1 PRs
Created by nasmovk: 1 PRs
Comments by github-actions[bot]: 290 comments
Comments by jmcouffin: 5 comments
Comments by devloai[bot]: 3 comments

June 2025: 17 PRs
Merged by jmcouffin: 17 PRs
Created by jmcouffin: 7 PRs
Created by dependabot[bot]: 7 PRs
Created by frank-e-loftus: 1 PRs
Created by OnePowerUser88: 1 PRs
Created by jaredholloway94: 1 PRs
Comments by github-actions[bot]: 160 comments
Comments by devloai[bot]: 7 comments
Comments by jmcouffin: 4 comments
Comments by jaredholloway94: 2 comments

July 2025: 11 PRs
Merged by jmcouffin: 11 PRs
Created by dependabot[bot]: 6 PRs
Created by rayala30: 1 PRs
Created by 924312: 1 PRs
Created by Wurschdhaud: 1 PRs
Created by tay0thman: 1 PRs
Created by jmcouffin: 1 PRs
Comments by github-actions[bot]: 530 comments
Comments by sanzoghenzo: 4 comments
Comments by Wurschdhaud: 2 comments
Comments by jmcouffin: 1 comments

August 2025: 31 PRs
Merged by jmcouffin: 29 PRs
Merged by tay0thman: 2 PRs
Created by jmcouffin: 11 PRs
Created by dependabot[bot]: 5 PRs
Created by MohamedAsli: 4 PRs
Created by tay0thman: 3 PRs
Created by Wurschdhaud: 3 PRs
Created by Denver-22: 3 PRs
Created by czwangxtt: 1 PRs
Created by Behind100proxies: 1 PRs
Comments by github-actions[bot]: 949 comments
Comments by jmcouffin: 22 comments
Comments by devloai[bot]: 8 comments
Comments by MohamedAsli: 7 comments
Comments by Denver-22: 3 comments
Comments by sanzoghenzo: 2 comments
Comments by tay0thman: 1 comments
Comments by Behind100proxies: 1 comments

September 2025: 20 PRs
Merged by jmcouffin: 19 PRs
Merged by MohamedAsli: 1 PRs
Created by Wurschdhaud: 7 PRs
Created by dependabot[bot]: 6 PRs
Created by MohamedAsli: 3 PRs
Created by IGF-Zhang: 1 PRs
Created by jmcouffin: 1 PRs
Created by frank-e-loftus: 1 PRs
Created by nasmovk: 1 PRs
Comments by github-actions[bot]: 152 comments
Comments by jmcouffin: 15 comments
Comments by Wurschdhaud: 2 comments
Comments by MohamedAsli: 1 comments

Total PRs merged in 2025: 180

PRs merged by each person (yearly total):

jmcouffin: 172 PRs
sanzoghenzo: 5 PRs
tay0thman: 2 PRs
MohamedAsli: 1 PRs

PRs created by each person (yearly total):

jmcouffin: 50 PRs
dependabot[bot]: 47 PRs
Wurschdhaud: 15 PRs
tay0thman: 13 PRs
sanzoghenzo: 9 PRs
MohamedAsli: 7 PRs
nasmovk: 5 PRs
thumDer: 4 PRs
devloai[bot]: 3 PRs
Malouche-git: 3 PRs
924312: 3 PRs
Denver-22: 3 PRs
jonatanjacobsson: 2 PRs
frank-e-loftus: 2 PRs
reformstudios: 1 PRs
mangrove-art: 1 PRs
jorisvandermeulen: 1 PRs
trgiangv: 1 PRs
nodatasheet: 1 PRs
leyarx: 1 PRs
iorhanV: 1 PRs
dnenov: 1 PRs
OnePowerUser88: 1 PRs
jaredholloway94: 1 PRs
rayala30: 1 PRs
czwangxtt: 1 PRs
Behind100proxies: 1 PRs
IGF-Zhang: 1 PRs

PR comments by each person (yearly total):

github-actions[bot]: 3358 comments
jmcouffin: 155 comments
devloai[bot]: 42 comments
sanzoghenzo: 39 comments
MohamedAsli: 8 comments
Malouche-git: 7 comments
tay0thman: 7 comments
nasmovk: 6 comments
Wurschdhaud: 4 comments
thumDer: 3 comments
Denver-22: 3 comments
dosymep: 2 comments
jorisvandermeulen: 2 comments
trgiangv: 2 comments
dnenov: 2 comments
jaredholloway94: 2 comments
nodatasheet: 1 comments
Behind100proxies: 1 comments

13 Likes

Hopefully more and more people get involved.

Thank you @Jean-Marc for your leadership and ability to close 91 issues this year so far(very impressive)

3 Likes