Skip to content

Commit

Permalink
refactor: switch to ror api v2
Browse files Browse the repository at this point in the history
pull a few more details from the ror api including wikidata ids for
future semantic web support, acronyms, and aliases

add ror api url to settings

should probably make a ror api service at some point but seemed overkill
and also the ror schema might benefit from codemeticulous changes in the
github mirroring PR
  • Loading branch information
alee committed Dec 11, 2024
1 parent f809f1d commit d0164e6
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 75 deletions.
3 changes: 3 additions & 0 deletions django/core/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,9 @@ def is_test(self):
DATACITE_DRY_RUN = os.getenv("DATACITE_DRY_RUN", "true")
DATACITE_API_PASSWORD = read_secret("datacite_api_password")

ROR_API_URL = "https://api.ror.org/v2/organizations"


SOCIALACCOUNT_PROVIDERS = {
# https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/about-scopes-for-oauth-apps/
"github": {
Expand Down

This file was deleted.

110 changes: 110 additions & 0 deletions django/curator/management/commands/ror_update_affiliation_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import argparse
import json
import logging
import requests

from django.conf import settings
from django.core.management.base import BaseCommand

from core.models import MemberProfile


logger = logging.getLogger(__name__)


class Command(BaseCommand):
help = """Update all MemberProfile affiliations with lat/lon locations pulled from the ROR API"""

def add_arguments(self, parser):
parser.add_argument(
"--force",
action=argparse.BooleanOptionalAction,
default=False,
dest="force",
help="Force update of all affiliations with geo lat/lon data, name, link, and type",
)

def handle(self, *args, **options):
"""
Inspects and updates all active MemberProfiles with affiliations with lat/lon coordinate data from the ROR API
"""
force = options["force"]
with requests.Session() as session:

for profile in MemberProfile.objects.public().with_affiliations():

should_update_profile = False
for affiliation in profile.affiliations:
if "ror_id" not in affiliation:
continue
if "coordinates" not in affiliation or force:
updated_affiliation_data = self.lookup_ror_id(
affiliation["ror_id"], session
)
if updated_affiliation_data:
affiliation.update(**updated_affiliation_data)
should_update_profile = True

if should_update_profile:
profile.save()

def _build_ror_api_url(self, query):
return f"{settings.ROR_API_URL}/{query}"

def lookup_ror_id(self, ror_id, session):
# FIXME: should consider creating a simple ROR module to handle interactions with the ROR API
# and extraction of metadata from their schema though currently only used here,
# populate_memberprofile_affiliations, and in the frontend ror.ts api
# may also benefit from the pydantic schema work that @sgfost is doing with codemeticulous
api_url = self._build_ror_api_url(ror_id)
try:
response = session.get(api_url, timeout=10)
response.raise_for_status()
data = response.json()
logger.debug("ROR response: %s", data)
print("\nJSON: ", json.dumps(data, indent=4), "\n")
print(".", end="", flush=True)
location = data["locations"][0]
geonames_details = location["geonames_details"]
ror_data = {
"name": "",
"aliases": [],
"acronyms": [],
"link": "",
"types": data["types"],
"wikipedia_url": "",
"wikidata": "",
}
if geonames_details:
ror_data.update(
coordinates={
"lat": geonames_details["lat"],
"lon": geonames_details["lng"],
},
geonames_details={
"id": location["geonames_id"],
**geonames_details,
},
)
for name_object in data["names"]:
if "ror_display" in name_object["types"]:
ror_data["name"] = name_object["value"]
if "alias" in name_object["types"]:
ror_data["aliases"].append(name_object)
if "acronym" in name_object["types"]:
ror_data["acronyms"].append(name_object)
for link_object in data["links"]:
if link_object["type"] == "website":
ror_data["link"] = link_object["value"]
if link_object["type"] == "wikipedia":
ror_data["wikipedia_url"] = link_object["value"]
for external_id_object in data["external_ids"]:
if external_id_object["type"] == "wikidata":
ror_data["wikidata"] = external_id_object["all"][0]

return ror_data

except requests.RequestException:
print("E", end="", flush=True)
logger.warning("Unable to retrieve ROR data for %s", ror_id)
return {}
13 changes: 9 additions & 4 deletions django/home/metrics.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from core.models import MemberProfile, ComsesGroups
from library.models import CodebaseRelease, CodebaseReleaseDownload, Codebase
import logging
import pandas as pd
from collections import defaultdict
from django.db import connection
from django.core.cache import cache
from django.db.models import Count, F
import pandas as pd

from core.models import MemberProfile, ComsesGroups
from library.models import CodebaseRelease, CodebaseReleaseDownload, Codebase


logger = logging.getLogger(__name__)


class Metrics:
Expand Down Expand Up @@ -354,7 +359,7 @@ def get_release_programming_language_timeseries(self, start_year):
.order_by("year")
)

# temporary fix to combine netlogo and logo
# FIXME: temporary fix to combine netlogo and logo
combined_metrics = defaultdict(lambda: defaultdict(int))

for metric in programming_language_metrics:
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/UserMapView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import labelInit from "highcharts/modules/series-label";
import { Chart } from "highcharts-vue";
import world from "@/assets/world.geo.json";
import proj4 from "proj4";
import type { MetricsData, MetricsChartSelection, Metric } from "@/types";
import type { MetricsData } from "@/types";
exportingInit(Highcharts); // required for hamburger menu w/ download options
labelInit(Highcharts); // required for series labels on area charts
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/composables/api/ror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export function useRORAPI() {
* Composable function for making requests to the Research Organization Registry REST API
* https://ror.readme.io/docs/rest-api
*
* FIXME: update to ror v2 schema eventually https://github.com/comses/planning/issues/294
*
* @returns - An object containing reactive state of the request and helper functions for API requests
*/

Expand Down

0 comments on commit d0164e6

Please sign in to comment.