import requests import pandas as pd import sys import json # --- CONFIGURATION --- BASE_URL = "https://openproject.nabd-co.com/" API_KEY = "dfc009a268f8490c2502458bad364c2f0ae27762c6b4a38c4dac6d394ca3058f" PROJECT_IDENTIFIER = "asf" AUTH = ('apikey', API_KEY) def get_type_id_map(): url = f"{BASE_URL}/api/v3/types" try: response = requests.get(url, auth=AUTH) response.raise_for_status() return {t['name'].lower(): str(t['id']) for t in response.json()['_embedded']['elements']} except Exception as e: print(f"❌ Error fetching type definitions: {e}") return {} def fetch_work_packages(types_filter=None): url = f"{BASE_URL}/api/v3/projects/{PROJECT_IDENTIFIER}/work_packages" params = {"pageSize": 1000} # Fetch more items in one go if types_filter: type_map = get_type_id_map() type_ids = [type_map[t.lower()] for t in types_filter if t.lower() in type_map] if not type_ids: print(f"⚠️ Warning: Types {types_filter} not found. Fetching all types instead.") else: filters = [{"type": {"operator": "=", "values": type_ids}}] params['filters'] = json.dumps(filters) try: response = requests.get(url, auth=AUTH, params=params) response.raise_for_status() return response.json()['_embedded']['elements'] except Exception as e: print(f"❌ API Error: {e}") return [] def get_relations(wp_id): url = f"{BASE_URL}/api/v3/work_packages/{wp_id}/relations" try: resp = requests.get(url, auth=AUTH) relations = resp.json()['_embedded']['elements'] rel_list = [] for r in relations: rel_type = r['type'] # Safely get the ID of the related work package to_link = r['_links'].get('to', {}).get('href', '') from_link = r['_links'].get('from', {}).get('href', '') other_id = to_link.split('/')[-1] if f"/{wp_id}" not in to_link else from_link.split('/')[-1] if other_id: rel_list.append(f"{rel_type}(#{other_id})") return "; ".join(rel_list) except: return "" def export_data(types_list=None, output_format='csv'): print(f"🔍 Fetching data for: {types_list if types_list else 'All Types'}...") wps = fetch_work_packages(types_list) if not wps: print("No work packages found.") return data = [] for wp in wps: wp_id = wp['id'] # --- SAFE PARENT ID CHECK --- parent_id = "" parent_link = wp['_links'].get('parent') # Check if parent exists AND has an href attribute if parent_link and parent_link.get('href'): parent_id = parent_link['href'].split('/')[-1] # Relations call relations = get_relations(wp_id) data.append({ "ID": wp_id, "Type": wp['_links']['type']['title'], "Status": wp['_links']['status']['title'], "Title": wp['subject'], "Description": wp['description']['raw'] if wp.get('description') else "", "Parent_ID": parent_id, "Relations": relations }) df = pd.DataFrame(data) filename = f"traceability_export.{output_format}" if output_format == 'xlsx': df.to_excel(filename, index=False) else: df.to_csv(filename, index=False, encoding='utf-8-sig') print(f"✅ Successfully exported {len(df)} items to {filename}") def get_type_mapping(): url = f"{BASE_URL}/api/v3/types" try: response = requests.get(url, auth=AUTH) response.raise_for_status() return {t['name'].lower(): t['id'] for t in response.json()['_embedded']['elements']} except Exception as e: print(f"❌ Error fetching types: {e}") sys.exit(1) if __name__ == "__main__": type_map = get_type_mapping() print(type(type_map)) print(list(type_map.keys())) # Usage: python get_traceability.py requirements task requested_types = [] #list(type_map.keys()) #sys.argv[1:] if len(sys.argv) > 1 else None export_data(types_list=requested_types, output_format='csv')