33"""
44
55import re
6- import urllib .parse
76from dataclasses import dataclass
87from typing import Callable , Dict , Optional
98
9+ import pycountry
1010import requests
1111
1212from codecarbon .core .cloud import get_env_cloud_details
@@ -93,10 +93,14 @@ def from_geo_js(cls, url: str) -> "GeoMetadata":
9393 try :
9494 response : Dict = requests .get (url , timeout = 0.5 ).json ()
9595
96+ region = response .get ("region" , "" ).lower ()
97+ if not region :
98+ raise ValueError ("Region is empty" )
99+
96100 return cls (
97101 country_iso_code = response ["country_code3" ].upper (),
98102 country_name = response ["country" ],
99- region = response . get ( " region" , "" ). lower () ,
103+ region = region ,
100104 latitude = float (response .get ("latitude" )),
101105 longitude = float (response .get ("longitude" )),
102106 country_2letter_iso_code = response .get ("country_code" ),
@@ -107,32 +111,36 @@ def from_geo_js(cls, url: str) -> "GeoMetadata":
107111 f"Unable to access geographical location through primary API. Will resort to using the backup API - Exception : { e } - url={ url } "
108112 )
109113
110- geo_url_backup = "https://ip-api.com /json/ "
114+ geo_url_backup = "https://ipinfo.io /json"
111115
112116 try :
113117 geo_response : Dict = requests .get (geo_url_backup , timeout = 0.5 ).json ()
114- country_name = geo_response ["country" ]
115118
116- # The previous request does not return the three-letter country code
117- country_code_3_url = f"https://api.first.org/data/v1/countries?q={ urllib .parse .quote_plus (country_name )} &scope=iso"
118- country_code_response : Dict = requests .get (
119- country_code_3_url , timeout = 0.5
120- ).json ()
119+ # extract latitude and longitude from loc (e.g., "loc": "37.4056,-122.0775")
120+ loc = geo_response .get ("loc" , "" ).split ("," )
121+ latitude = float (loc [0 ]) if len (loc ) == 2 else 0.0
122+ longitude = float (loc [1 ]) if len (loc ) == 2 else 0.0
123+
124+ # Retrieve the 3-letter ISO code using pycountry
125+ country_2letter_iso_code = geo_response .get ("country" )
126+ country = pycountry .countries .get (alpha_2 = country_2letter_iso_code )
127+
128+ # Some countries might not be found or mapped perfectly
129+ country_iso_code = country .alpha_3 if country else ""
130+ country_name = country .name if country else ""
121131
122132 return cls (
123- country_iso_code = next (
124- iter (country_code_response ["data" ].keys ())
125- ).upper (),
133+ country_iso_code = country_iso_code .upper (),
126134 country_name = country_name ,
127- region = geo_response .get ("regionName " , "" ).lower (),
128- latitude = float ( geo_response . get ( "lat" )) ,
129- longitude = float ( geo_response . get ( "lon" )) ,
130- country_2letter_iso_code = geo_response . get ( "countryCode" ) ,
135+ region = geo_response .get ("region " , "" ).lower (),
136+ latitude = latitude ,
137+ longitude = longitude ,
138+ country_2letter_iso_code = country_2letter_iso_code ,
131139 )
132140 except Exception as e :
133141 # If both API calls fail, default to Canada
134142 logger .warning (
135- f"Unable to access geographical location. Using 'Canada' as the default value - Exception : { e } - url={ url } "
143+ f"Unable to access geographical location through fallback API . Using 'Canada' as the default value - Exception : { e } - url={ geo_url_backup } "
136144 )
137145
138146 return cls (
0 commit comments