Last active
October 9, 2025 18:31
-
-
Save chrismytton/d7780ea9706a7fff41e9a0645217755e to your computer and use it in GitHub Desktop.
Get a list of pull requests created by the authenticated user for a given year
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # /// script | |
| # requires-python = ">=3.12" | |
| # dependencies = [ | |
| # "requests", | |
| # ] | |
| # /// | |
| import requests | |
| from datetime import datetime | |
| import os | |
| from typing import List, Dict | |
| import argparse | |
| import subprocess | |
| import shutil | |
| from io import StringIO | |
| def fetch_prs(github_token: str, username: str, year: int, org: str | None = None) -> List[Dict]: | |
| """ | |
| Fetch all pull requests created by a user in a specific year across all repositories. | |
| Args: | |
| github_token (str): GitHub personal access token | |
| username (str): GitHub username | |
| year (int): Year to fetch PRs for | |
| org (str | None): Optional organization to filter PRs by | |
| Returns: | |
| List[Dict]: List of pull request information | |
| """ | |
| headers = { | |
| 'Authorization': f'token {github_token}', | |
| 'Accept': 'application/vnd.github.v3+json' | |
| } | |
| # Search query parameters | |
| query = f'author:{username} is:pr created:{year}-01-01..{year}-12-31' | |
| if org: | |
| query += f' org:{org}' | |
| base_url = 'https://api.github.com/search/issues' | |
| all_prs = [] | |
| page = 1 | |
| while True: | |
| params = { | |
| 'q': query, | |
| 'per_page': 100, | |
| 'page': page | |
| } | |
| response = requests.get(base_url, headers=headers, params=params) | |
| if response.status_code != 200: | |
| raise Exception(f'API request failed: {response.status_code}\n{response.text}') | |
| data = response.json() | |
| items = data.get('items', []) | |
| if not items: | |
| break | |
| for pr in items: | |
| pr_info = { | |
| 'title': pr['title'], | |
| 'url': pr['html_url'], | |
| 'repo': pr['repository_url'].split('/')[-1], | |
| 'state': pr['state'], | |
| 'created_at': pr['created_at'], | |
| 'updated_at': pr['updated_at'] | |
| } | |
| all_prs.append(pr_info) | |
| page += 1 | |
| return all_prs | |
| def get_configuration() -> tuple[str, str, int, str | None]: | |
| """ | |
| Parse command line arguments and get GitHub token from environment. | |
| Returns: | |
| tuple[str, str, int, str | None]: GitHub token, username, year, and optional org | |
| """ | |
| current_year = datetime.now().year | |
| parser = argparse.ArgumentParser(description='Fetch GitHub PRs created by a user in a specific year') | |
| parser.add_argument('username', help='GitHub username to fetch PRs for') | |
| parser.add_argument('--year', type=int, default=current_year, | |
| help=f'Year to fetch PRs for (default: {current_year})') | |
| parser.add_argument('--org', type=str, default=None, | |
| help='GitHub organization to filter PRs by (optional)') | |
| args = parser.parse_args() | |
| github_token = os.getenv('GITHUB_TOKEN') | |
| if not github_token: | |
| raise ValueError("Please set the GITHUB_TOKEN environment variable") | |
| return github_token, args.username, args.year, args.org | |
| def sort_prs_by_date(prs: List[Dict]) -> List[Dict]: | |
| """ | |
| Sort pull requests by creation date. | |
| Args: | |
| prs (List[Dict]): List of pull requests | |
| Returns: | |
| List[Dict]: Sorted list of pull requests | |
| """ | |
| return sorted(prs, key=lambda x: x['created_at']) | |
| def format_prs_markdown(prs: List[Dict], username: str, year: int, org: str | None = None) -> str: | |
| """ | |
| Format pull requests as markdown string. | |
| Args: | |
| prs (List[Dict]): List of pull requests | |
| username (str): GitHub username | |
| year (int): Year of the PRs | |
| org (str | None): Optional organization filter | |
| Returns: | |
| str: Markdown formatted string | |
| """ | |
| output = StringIO() | |
| header = f"# Pull Requests by {username} in {year}" | |
| if org: | |
| header += f" (Organization: {org})" | |
| output.write(f"{header}\n\n") | |
| output.write(f"Total PRs: {len(prs)}\n\n") | |
| for pr in prs: | |
| created_date = datetime.fromisoformat(pr['created_at'].replace('Z', '+00:00')).strftime('%Y-%m-%d') | |
| output.write(f"### {pr['title']}\n") | |
| output.write(f"- **Repository:** {pr['repo']}\n") | |
| output.write(f"- **Status:** {pr['state']}\n") | |
| output.write(f"- **Created:** {created_date}\n") | |
| output.write(f"- **URL:** {pr['url']}\n") | |
| output.write("\n") | |
| return output.getvalue() | |
| def display_output(content: str) -> None: | |
| """ | |
| Display content, piping through glow if available. | |
| Args: | |
| content (str): Content to display | |
| """ | |
| # Check if glow is installed | |
| if shutil.which('glow'): | |
| try: | |
| # Pipe through glow with pager | |
| process = subprocess.Popen( | |
| ['glow', '-p'], | |
| stdin=subprocess.PIPE, | |
| text=True | |
| ) | |
| process.communicate(input=content) | |
| except Exception: | |
| # If glow fails, fall back to regular print | |
| print(content) | |
| else: | |
| # Glow not installed, print normally | |
| print(content) | |
| def main(): | |
| try: | |
| github_token, username, year, org = get_configuration() | |
| prs = fetch_prs(github_token, username, year, org) | |
| sorted_prs = sort_prs_by_date(prs) | |
| markdown_output = format_prs_markdown(sorted_prs, username, year, org) | |
| display_output(markdown_output) | |
| except Exception as e: | |
| print(f"Error: {str(e)}") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment