Created
June 18, 2025 14:09
-
-
Save rhodessteve/66189c56337bddc962df3cafee555ae3 to your computer and use it in GitHub Desktop.
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
import requests | |
import json | |
import base64 | |
import time | |
#import re # Import the regular expression library | |
JIRA_DOMAIN = "yourdomain.atlassian.net" | |
API_TOKEN = "yourlongtokenhere" | |
EMAIL = "youremail@domain.com" | |
PROJECT_KEYS = ["ABC", "KLF", "UW"] # Add all your desired project keys here | |
# --- EMAIL DOMAIN EXCLUSION CONFIGURATION --- | |
# Set this to a domain name (e.g., "domain.com") to exclude users with that email domain. | |
# Set this to "none" (lowercase string) to include all active human users regardless of their email domain. | |
DOMAIN_EXCLUDE = "domain.com" | |
# DOMAIN_EXCLUDE = "none" # Uncomment this line and comment the above to include all users regardless of domain | |
# --------------------------------------------- | |
AUTH_STRING = base64.b64encode(f"{EMAIL}:{API_TOKEN}".encode()).decode() | |
HEADERS = { | |
"Accept": "application/json", | |
"Content-Type": "application/json", | |
"Authorization": f"Basic {AUTH_STRING}", | |
} | |
# --- Global Cache for User Details --- | |
# This prevents fetching the same user's details multiple times if they are in multiple projects | |
user_details_cache = {} | |
# ------------------------------------- | |
# This will store the final filtered users, grouped by project | |
all_project_filtered_users = {} | |
print(f"Starting multi-project user retrieval for: {', '.join(PROJECT_KEYS)}") | |
try: | |
for current_project_key in PROJECT_KEYS: | |
print(f"\n--- Processing Project: {current_project_key} ---") | |
potential_account_ids_for_project = set() | |
current_project_filtered_users = [] | |
# 1. Get Project Roles for the current project | |
roles_url = f"https://{JIRA_DOMAIN}/rest/api/3/project/{current_project_key}/role" | |
print(f" Calling roles API for {current_project_key}: {roles_url}") | |
roles_response = requests.get(roles_url, headers=HEADERS) | |
roles_response.raise_for_status() | |
project_roles = roles_response.json() | |
print(" Project Roles successfully retrieved.") | |
# Iterate through each role | |
for role_name, role_url in project_roles.items(): | |
role_id = role_url.split("/")[-1] | |
# 2. Get Actors for the specific role in the current project | |
role_actors_url = f"https://{JIRA_DOMAIN}/rest/api/3/project/{current_project_key}/role/{role_id}" | |
actors_response = requests.get(role_actors_url, headers=HEADERS) | |
actors_response.raise_for_status() | |
role_data = actors_response.json() | |
# Collect all 'atlassian-user-role-actor' account IDs for this project | |
for actor in role_data.get("actors", []): | |
if actor.get("type") == "atlassian-user-role-actor": | |
actor_user_data = actor.get("actorUser") | |
if actor_user_data and "accountId" in actor_user_data: | |
account_id = actor_user_data["accountId"] | |
potential_account_ids_for_project.add(account_id) | |
print( | |
f" DEBUG: Finished collecting potential account IDs for {current_project_key}." | |
f" Found {len(potential_account_ids_for_project)} unique IDs." | |
) | |
# 3. For each unique account in this project, query details (using cache) and apply filters | |
if potential_account_ids_for_project: | |
print(f" Checking 'active' status, 'accountType', and custom email criteria for {current_project_key}...") | |
for account_id in potential_account_ids_for_project: | |
user_data = None | |
if account_id in user_details_cache: | |
user_data = user_details_cache[account_id] | |
# print(f" (Cached) Details for {account_id}") # Optional: show when cache is used | |
else: | |
# Make API call only if not in cache | |
user_details_url = f"https://{JIRA_DOMAIN}/rest/api/3/user?accountId={account_id}" | |
user_details_response = requests.get(user_details_url, headers=HEADERS) | |
if user_details_response.status_code == 200: | |
user_data = user_details_response.json() | |
user_details_cache[account_id] = user_data # Add to cache | |
# print(f" (Fetched) Details for {account_id}") # Optional: show when fetched | |
elif user_details_response.status_code == 403: | |
print(f" WARNING: Permission denied to fetch details for {account_id}. Skipping.") | |
print(f" Response content: {user_details_response.text}") | |
else: | |
print(f" ERROR fetching details for {account_id}: {user_details_response.status_code}") | |
print(f" Response content: {user_details_response.text}") | |
time.sleep(0.1) # Small delay after each API call | |
if user_data: # Only proceed if user_data was successfully obtained | |
user_email = user_data.get("emailAddress") | |
is_active = user_data.get("active") | |
is_human_account = user_data.get("accountType") == "atlassian" | |
meets_email_criteria = False | |
if DOMAIN_EXCLUDE.lower() == "none": | |
meets_email_criteria = True | |
else: | |
exclude_domain_lower = DOMAIN_EXCLUDE.lower() | |
if ( | |
not user_email # User has no email listed | |
or (user_email and not user_email.lower().endswith(f"@{exclude_domain_lower}")) # User has email, but not the excluded domain | |
): | |
meets_email_criteria = True | |
if is_active and is_human_account and meets_email_criteria: | |
current_project_filtered_users.append({ | |
"displayName": user_data.get("displayName"), | |
"accountId": user_data.get("accountId"), | |
"emailAddress": user_email, | |
}) | |
else: | |
print(" No potential account IDs collected for this project.") | |
# Store the filtered users for the current project | |
all_project_filtered_users[current_project_key] = current_project_filtered_users | |
# --- Final Summary for All Projects --- | |
print("\n" + "="*50) | |
print("--- Consolidated Summary of Filtered Active Human Users by Project ---") | |
print("="*50) | |
total_overall_filtered_users = 0 | |
for project_key, users_list in all_project_filtered_users.items(): | |
print(f"\nProject: {project_key} (Total: {len(users_list)})") | |
if users_list: | |
for user_info in users_list: | |
print( | |
f" Display Name: {user_info['displayName']}, Account ID:" | |
f" {user_info['accountId']}, Email:" | |
f" {user_info.get('emailAddress', 'N/A')}" | |
) | |
total_overall_filtered_users += len(users_list) | |
else: | |
print(" No active human users matching the criteria found for this project.") | |
print(f"\n--- Overall Total Active Human Users Across All Projects: {total_overall_filtered_users} ---") | |
print("="*50) | |
except requests.exceptions.HTTPError as err: | |
print(f"HTTP Error occurred: {err}") | |
print(f"Response content: {err.response.text}") | |
except requests.exceptions.RequestException as err: | |
print(f"An error occurred: {err}") | |
except json.JSONDecodeError: | |
print("Failed to parse JSON response. Check if API Token/Email are correct or response format.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment