Coverage for heritrace/apis/orcid.py: 100%

51 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-18 11:10 +0000

1from functools import lru_cache 

2from urllib.parse import urlparse 

3 

4import requests 

5 

6 

7def is_orcid_url(url): 

8 """Check if a URL is an ORCID URL.""" 

9 try: 

10 parsed = urlparse(url) 

11 return parsed.netloc == "orcid.org" or parsed.netloc == "https://orcid.org" 

12 except: 

13 return False 

14 

15 

16def extract_orcid_id(url): 

17 """Extract ORCID ID from URL.""" 

18 try: 

19 parsed = urlparse(url) 

20 path = parsed.path.strip("/") 

21 if path.startswith("https://orcid.org/"): 

22 path = path[len("https://orcid.org/") :] 

23 return path 

24 except: 

25 return None 

26 

27 

28@lru_cache(maxsize=1000) 

29def get_orcid_data(orcid_id): 

30 """ 

31 Fetch researcher data from ORCID API with caching. 

32 

33 Args: 

34 orcid_id (str): The ORCID identifier 

35 

36 Returns: 

37 dict: Researcher data including name and other details 

38 """ 

39 headers = {"Accept": "application/json"} 

40 

41 try: 

42 response = requests.get( 

43 f"https://pub.orcid.org/v3.0/{orcid_id}/person", headers=headers, timeout=5 

44 ) 

45 

46 if response.status_code == 200: 

47 data = response.json() 

48 

49 # Extract relevant information 

50 result = { 

51 "name": None, 

52 "other_names": [], 

53 "biography": None, 

54 "orcid": orcid_id, 

55 } 

56 

57 # Get main name 

58 if "name" in data: 

59 given_name = data["name"].get("given-names", {}).get("value", "") 

60 family_name = data["name"].get("family-name", {}).get("value", "") 

61 if given_name or family_name: 

62 result["name"] = f"{given_name} {family_name}".strip() 

63 

64 # Get other names 

65 if "other-names" in data and "other-name" in data["other-names"]: 

66 result["other_names"] = [ 

67 name.get("content", "") 

68 for name in data["other-names"]["other-name"] 

69 if "content" in name 

70 ] 

71 

72 # Get biography 

73 if "biography" in data and data["biography"]: 

74 result["biography"] = data["biography"].get("content", "") 

75 

76 return result 

77 

78 except Exception: 

79 return None 

80 

81 return None 

82 

83 

84def format_orcid_attribution(url): 

85 """ 

86 Format ORCID attribution for display. 

87 

88 Args: 

89 url (str): The ORCID URL 

90 

91 Returns: 

92 str: Formatted HTML for displaying ORCID attribution 

93 """ 

94 

95 orcid_id = extract_orcid_id(url) 

96 if not orcid_id: 

97 return f'<a href="{url}" target="_blank">{url}</a>' 

98 

99 researcher_data = get_orcid_data(orcid_id) 

100 if not researcher_data: 

101 return f'<a href="{url}" target="_blank">{url}</a>' 

102 

103 name = researcher_data["name"] or url 

104 

105 html = f'<a href="{url}" target="_blank" class="orcid-attribution">' 

106 html += f'<img src="/static/images/orcid-logo.png" alt="ORCID iD" class="orcid-icon mx-1 mb-1" style="width: 16px; height: 16px;">' 

107 html += f"{name} [orcid:{orcid_id}]</a>" 

108 

109 return html