Skip to content

Instantly share code, notes, and snippets.

@edsu
Last active February 16, 2024 22:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edsu/7d2d8fb2049075e76bfdf106b8805220 to your computer and use it in GitHub Desktop.
Save edsu/7d2d8fb2049075e76bfdf106b8805220 to your computer and use it in GitHub Desktop.
Get some usable JSON for a given LC name or subject authority string: e.g. `./lcauthority.py "Southampton (England)"`
#!/usr/bin/env python3
"""
A small command line tool to get the JSON-LD for a Library of Congress authority
record by first looking up the authority as a string using the label lookup
service and then getting the JSON-LD for the authority and writing it out using
a JSON-LD frame where the SKOS is the default vocabulary.
"""
import sys
import json
import requests
from pyld import jsonld
def get_authority(label):
# lookup the id.loc.gov URI for the label (the RDF includes HTTP URIs)
resp = requests.get(f'https://id.loc.gov/authorities/label/{label}', allow_redirects=False)
if resp.status_code == 404:
return None
uri = resp.headers['Location'].replace('https://', 'http://')
# fetch the JSON-LD
doc = requests.get(uri, headers={"Accept": "application/ld+json"}).json()
# use this context to parse the JSONLD into something more usable
context = {
"mads": "http://www.loc.gov/mads/rdf/v1#",
"@vocab": "http://www.w3.org/2004/02/skos/core#",
"skosxl": "http://www.w3.org/2008/05/skos-xl#",
"recordinfo": "http://id.loc.gov/ontologies/RecordInfo#",
"identifiers": "http://id.loc.gov/vocabulary/identifiers/",
"bflc": "http://id.loc.gov/ontologies/bflc/",
"iso6392": "http://id.loc.gov/vocabulary/iso639-2/",
"changeset": "http://purl.org/vocab/changeset/schema#"
}
# "frame" the JSON-LD
return jsonld.frame(doc, {
"@context": context,
"@id": uri,
# so we get linked SKOS Concepts in addition to linked MADS Authorities
"@embed": "@always",
})
if __name__ == "__main__":
if len(sys.argv) != 2:
sys.exit("Usage: lcauthority.py <authority string>")
print(json.dumps(get_authority(sys.argv[1]), indent=2))
{
"@context": {
"mads": "http://www.loc.gov/mads/rdf/v1#",
"@vocab": "http://www.w3.org/2004/02/skos/core#",
"skosxl": "http://www.w3.org/2008/05/skos-xl#",
"recordinfo": "http://id.loc.gov/ontologies/RecordInfo#",
"identifiers": "http://id.loc.gov/vocabulary/identifiers/",
"bflc": "http://id.loc.gov/ontologies/bflc/",
"iso6392": "http://id.loc.gov/vocabulary/iso639-2/",
"changeset": "http://purl.org/vocab/changeset/schema#"
},
"@id": "http://id.loc.gov/authorities/names/n79072768",
"@type": [
"mads:Geographic",
"mads:Authority",
"Concept"
],
"bflc:marcKey": "151 $aSouthampton (England)",
"identifiers:lccn": "n 79072768",
"identifiers:local": "(OCoLC)oca00305516",
"mads:adminMetadata": [
{
"@type": "recordinfo:RecordInfo",
"recordinfo:languageOfCataloging": {
"@id": "iso6392:eng"
},
"recordinfo:recordChangeDate": {
"@type": "http://www.w3.org/2001/XMLSchema#dateTime",
"@value": "1979-08-16T00:00:00"
},
"recordinfo:recordContentSource": {
"@id": "http://id.loc.gov/vocabulary/organizations/dlc"
},
"recordinfo:recordStatus": {
"@type": "http://www.w3.org/2001/XMLSchema#string",
"@value": "new"
}
},
{
"@type": "recordinfo:RecordInfo",
"recordinfo:languageOfCataloging": {
"@id": "iso6392:eng"
},
"recordinfo:recordChangeDate": {
"@type": "http://www.w3.org/2001/XMLSchema#dateTime",
"@value": "2014-04-25T17:12:06"
},
"recordinfo:recordContentSource": {
"@id": "http://id.loc.gov/vocabulary/organizations/dlc"
},
"recordinfo:recordStatus": {
"@type": "http://www.w3.org/2001/XMLSchema#string",
"@value": "revised"
}
}
],
"mads:authoritativeLabel": "Southampton (England)",
"mads:code": {
"@type": "http://id.loc.gov/datatypes/codes/gac",
"@value": "e-uk-en"
},
"mads:editorialNote": "[Old catalog heading: Southampton, Eng.]",
"mads:elementList": {
"@list": [
{
"@type": "mads:GeographicElement",
"mads:elementValue": "Southampton (England)"
}
]
},
"mads:hasCloseExternalAuthority": [
{
"@id": "https://id.worldcat.org/fast/1343677",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": "England Southampton Region",
"prefLabel": "England Southampton Region"
},
{
"@id": "https://id.worldcat.org/fast/1205666",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": "England Southampton",
"prefLabel": "England Southampton"
},
{
"@id": "http://data.bnf.fr/ark:/12148/cb11962315f",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": {
"@language": "fr",
"@value": "Southampton (GB)"
},
"prefLabel": {
"@language": "fr",
"@value": "Southampton (GB)"
}
},
{
"@id": "http://www.wikidata.org/entity/Q79848",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": "Southampton",
"prefLabel": "Southampton"
},
{
"@id": "https://id.worldcat.org/fast/1343676",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": "England Southampton Metropolitan Area",
"prefLabel": "England Southampton Metropolitan Area"
}
],
"mads:hasEarlierEstablishedForm": {
"@type": [
"mads:Geographic",
"mads:Variant"
],
"mads:elementList": {
"@list": [
{
"@type": "mads:GeographicElement",
"mads:elementValue": "Southampton (Hampshire)"
}
]
},
"mads:variantLabel": "Southampton (Hampshire)"
},
"mads:hasExactExternalAuthority": {
"@id": "http://viaf.org/viaf/sourceID/LC%7Cn++79072768#skos:Concept"
},
"mads:hasSource": [
{
"@type": "mads:Source",
"mads:citationSource": "Adshead, S. D. The replanning of Southampton, 1942.",
"mads:citationStatus": "found"
},
{
"@type": "mads:Source",
"mads:citationNote": "(Southampton (England))",
"mads:citationSource": "BL hdg.",
"mads:citationStatus": "found"
},
{
"@type": "mads:Source",
"mads:citationNote": "t.p. (County Borough of Southampton)",
"mads:citationSource": "Annual report on school medical work ... 1923:",
"mads:citationStatus": "found"
},
{
"@type": "mads:Source",
"mads:citationNote": "(ppl; 50\u00b054\u02b900\u02baN 001\u00b024\u02b900\u02baW)",
"mads:citationSource": "GeoNames, algorithmically matched, 2009",
"mads:citationStatus": "found"
}
],
"mads:hasVariant": [
{
"@type": [
"mads:Geographic",
"mads:Variant"
],
"mads:elementList": {
"@list": [
{
"@type": "mads:GeographicElement",
"mads:elementValue": "Southampton (Hampshire)"
}
]
},
"mads:variantLabel": "Southampton (Hampshire)"
},
{
"@type": [
"mads:Geographic",
"mads:Variant"
],
"mads:elementList": {
"@list": [
{
"@type": "mads:GeographicElement",
"mads:elementValue": "County Borough of Southampton"
}
]
},
"mads:variantLabel": "County Borough of Southampton"
}
],
"mads:identifiesRWO": {
"@id": "http://id.loc.gov/rwo/agents/n79072768",
"@type": [
"mads:RWO",
"http://www.loc.gov/standards/mads/rdf/v1#Geographic"
],
"bflc:subjectOf": [
{
"@id": "http://id.loc.gov/resources/works/7394958",
"@type": "http://id.loc.gov/ontologies/bibframe/Work",
"bflc:aap": "Adshead, S. D. replanning of Southampton: report of S.D. Adshead ... and H. T. Cook ... the the Council of the county borough of Southampton. With a section on buildings and monuments of historical and architectural value, by O. G. S. Crawford, F.S.A. February 1942"
},
{
"@id": "http://id.loc.gov/resources/works/9692091",
"@type": "http://id.loc.gov/ontologies/bibframe/Work",
"bflc:aap": "Southampton Civic Society. Civic Survey Committee. Southampton; a civic survey, being a report of the Civic Survey Committee of the Southampton Civic Society under the chairmanship of Brigadier E.M. Jack .."
}
],
"http://www.w3.org/2000/01/rdf-schema#label": "Southampton (England)"
},
"mads:isMemberOfMADSCollection": [
{
"@id": "http://id.loc.gov/authorities/names/collection_NamesAuthorizedHeadings"
},
{
"@id": "http://id.loc.gov/authorities/names/collection_LCNAF"
}
],
"mads:isMemberOfMADSScheme": {
"@id": "http://id.loc.gov/authorities/names"
},
"altLabel": [
"Southampton (Hampshire)",
"County Borough of Southampton"
],
"changeNote": [
{
"@type": "changeset:ChangeSet",
"changeset:changeReason": {
"@type": "http://www.w3.org/2001/XMLSchema#string",
"@value": "new"
},
"changeset:createdDate": {
"@type": "http://www.w3.org/2001/XMLSchema#dateTime",
"@value": "1979-08-16T00:00:00"
},
"changeset:creatorName": {
"@id": "http://id.loc.gov/vocabulary/organizations/dlc"
},
"changeset:subjectOfChange": {
"@id": "http://id.loc.gov/authorities/names/n79072768"
}
},
{
"@type": "changeset:ChangeSet",
"changeset:changeReason": {
"@type": "http://www.w3.org/2001/XMLSchema#string",
"@value": "revised"
},
"changeset:createdDate": {
"@type": "http://www.w3.org/2001/XMLSchema#dateTime",
"@value": "2014-04-25T17:12:06"
},
"changeset:creatorName": {
"@id": "http://id.loc.gov/vocabulary/organizations/dlc"
},
"changeset:subjectOfChange": {
"@id": "http://id.loc.gov/authorities/names/n79072768"
}
}
],
"closeMatch": [
{
"@id": "https://id.worldcat.org/fast/1343677",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": "England Southampton Region",
"prefLabel": "England Southampton Region"
},
{
"@id": "https://id.worldcat.org/fast/1205666",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": "England Southampton",
"prefLabel": "England Southampton"
},
{
"@id": "http://data.bnf.fr/ark:/12148/cb11962315f",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": {
"@language": "fr",
"@value": "Southampton (GB)"
},
"prefLabel": {
"@language": "fr",
"@value": "Southampton (GB)"
}
},
{
"@id": "http://www.wikidata.org/entity/Q79848",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": "Southampton",
"prefLabel": "Southampton"
},
{
"@id": "https://id.worldcat.org/fast/1343676",
"@type": [
"mads:Authority",
"Concept"
],
"mads:authoritativeLabel": "England Southampton Metropolitan Area",
"prefLabel": "England Southampton Metropolitan Area"
}
],
"editorialNote": "[Old catalog heading: Southampton, Eng.]",
"exactMatch": {
"@id": "http://viaf.org/viaf/sourceID/LC%7Cn++79072768#skos:Concept"
},
"inScheme": {
"@id": "http://id.loc.gov/authorities/names"
},
"notation": {
"@type": "http://id.loc.gov/datatypes/codes/gac",
"@value": "e-uk-en"
},
"prefLabel": "Southampton (England)",
"skosxl:altLabel": [
{
"@type": "skosxl:Label",
"skosxl:literalForm": "Southampton (Hampshire)"
},
{
"@type": "skosxl:Label",
"skosxl:literalForm": "County Borough of Southampton"
}
]
}
@azaroth42
Copy link

azaroth42 commented Feb 15, 2024

I solved the same problem without JSON-LD compaction using the code below.

I agree that LC should define a useful context and compact their data so we all don't have to do #%^@(&#%^ like this!

    def reconstitute(self, js, nodes):
        # recursively build tree from @graph node list
        # only replace into tree once to ensure non-infinite-recursion
        # ignore number, string, etc values
        del nodes[js['@id']]
        for (k,v) in js.items():
            if type(v) == dict and '@id' in v and v['@id'] in nodes:
                js[k] = self.reconstitute(nodes[v['@id']], nodes)
            elif type(v) == list:
                new = []
                for vi in v:
                    if type(vi) == dict and '@id' in vi and vi['@id'] in nodes:
                        new.append(self.reconstitute(nodes[vi['@id']], nodes))
                    else:
                        new.append(vi)
                js[k] = new
        return js

  def process(self, rec):
        nodes = {}
        for n in rec['@graph']:
            try:
                nodes[n['@id']] = n
            except:
                pass
        if '@id' in rec and rec['@id'].startswith('/authorities/'):
            ident = rec['@id'].rsplit('/', 1)[1]
            topid = f"{namespace}{ident}"
        elif '@context' in rec and type(rec['@context']) == dict:
            topid = rec['@context']['about']
        else:
           # no way to know the top of the tree
           return None

        try:
            return self.reconstitute(nodes[topid], nodes)    
        except:
            return None
       

(some internal processing elided for ease)

@edsu
Copy link
Author

edsu commented Feb 15, 2024

Yeah, I noticed the compaction wasn't needed too. I updated the gist accordingly.

https://github.com/edsu/idloc/blob/main/idloc.py#L122-L130

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment