Skip to content

Commit

Permalink
msd3: add responsibleOrganization by default; use NSD to get org info
Browse files Browse the repository at this point in the history
  • Loading branch information
RayPlante committed May 10, 2024
1 parent 50a7fb5 commit 220ce2f
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 11 deletions.
2 changes: 1 addition & 1 deletion metadata
45 changes: 44 additions & 1 deletion python/nistoar/midas/dap/service/mds3.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from nistoar.pdr import def_schema_dir, def_etc_dir, constants as const
from nistoar.pdr.utils import build_mime_type_map, read_json
from nistoar.pdr.utils.prov import Agent, Action
from nistoar.nsd.client import NSDClient

from . import validate
from .. import nerdstore
Expand Down Expand Up @@ -226,14 +227,17 @@ class DAPService(ProjectService):
``file_manager``
a dictionary of properties configuring access to the file manager; if not used, a file
manager will not be used to get file information.
``default_responsible_org``
a dictionary containing NERDm Affiliation metadata to be provided as the default to the
``responsibleOrganization`` NERDm property.
Note that the DOI is not yet registered with DataCite; it is only internally reserved and included
in the record NERDm data.
"""

def __init__(self, dbclient_factory: DBClientFactory, config: Mapping={}, who: Agent=None,
log: Logger=None, nerdstore: NERDResourceStorage=None, project_type=DAP_PROJECTS,
minnerdmver=(0, 6), fmcli=None):
minnerdmver=(0, 6), fmcli=None, nsdcli=None):
"""
create the service
:param DBClientFactory dbclient_factory: the factory to create the DBIO service client from
Expand All @@ -248,6 +252,8 @@ def __init__(self, dbclient_factory: DBClientFactory, config: Mapping={}, who: A
subclass constructors.
:param FileManager fmcli: The FileManager client to use; if None, one will be constructed
from the configuration.
:param NSDClient nsdcli: The NSD client to use to look up people and organizations; if None,
one will be constructed from the configuration.
"""
super(DAPService, self).__init__(project_type, dbclient_factory, config, who, log,
_subsys="Digital Asset Publication Authoring System",
Expand All @@ -263,6 +269,13 @@ def __init__(self, dbclient_factory: DBClientFactory, config: Mapping={}, who: A
nerdstore = NERDResourceStorageFactory().open_storage(config.get("nerdstorage", {}), log)
self._store = nerdstore

self._nsdcli = nsdcli
if not self._nsdcli:
if not self.cfg.get('nsd', {}).get('service_endpoint'):
self.log.warning("NSD service is not configured; name lookups are not possible")
else:
self._nsdcli = self._make_nsd_client(config['nsd'])

self.cfg.setdefault('assign_doi', ASSIGN_DOI_REQUEST)
if not self.cfg.get('doi_naan') and self.cfg.get('assign_doi') != ASSIGN_DOI_NEVER:
raise ConfigurationException("Missing configuration: doi_naan")
Expand All @@ -282,6 +295,11 @@ def __init__(self, dbclient_factory: DBClientFactory, config: Mapping={}, who: A
def _make_fm_client(self, fmcfg):
return FileManager(fmcfg)

def _make_nsd_client(self, nsdcfg):
if not nsdcfg.get('service_endpoint'):
raise ConfigurationException("nsd config missing 'service_endpoint' parameter")
return NSDClient(nsdcfg['service_endpoint'])

def _choose_mediatype(self, fext):
defmt = 'application/octet-stream'

Expand Down Expand Up @@ -454,6 +472,28 @@ def _new_data_for(self, recid, meta=None, schemaid=None):
elif meta.get("contactName"):
out['contactPoint'] = self._moderate_contactPoint({"fn": meta["contactName"]}, doval=False)

ro = self.cfg.get('default_responsible_org', {})
if self._nsdcli and out.get('contactPoint', {}).get('hasEmail'):
try:
conrec = self._nsdcli.select_people(email=out.get['contactPoint']['hasEmail'])
except NSDServerError as ex:
self.log.warning("Unable to get org info on contact from NSD service: %s", str(ex))
except Exception as ex:
self.log.exception("Unexpected error while accessing NSD service: %s", str(ex))
else:
if len(conrec) > 0:
conrec = conrec[0]
if conrec:
ro['subunits'] = []
if not ro.get('title'):
ro['title'] = conrec.get("ouName", "")
else:
ro['subunits'].append(conrec.get("ouName", ""))
ro['subunits'].append(conrec.get("divisionName", ""))
ro['subunits'].append(conrec.get("groupName", ""))
if ro:
out['responsibleOrganization'] = [ ro ]

return out

def _get_sw_desc_for(self, link):
Expand Down Expand Up @@ -806,6 +846,9 @@ def _summarize(self, nerd: NERDResource):
# out["reference_count"] = nerd.references.count
out["file_count"] = nerd.files.count
out["nonfile_count"] = nerd.nonfiles.count
if resmd.get('responsibleOrganization', [{}])[0].get('title'):
out['responsibleOrganization'] = [ resmd['responsibleOrganization'][0]['title'] ] + \
resmd['responsibleOrganization'][0].get('subunits', [])
return out

_handsoff = ("@id @context publisher issued firstIssued revised annotated version " + \
Expand Down
13 changes: 10 additions & 3 deletions python/nistoar/nsd/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,18 @@ def groups(self) -> List[NISTOrg]:
raise NSDServerError(self.GROUP_EP, message="Unexpected org description: missing properties"
" (has service changed?)")

def select_people(self, name=None, lname=None, fname=None, ou=None, filter=None) -> List[Mapping]:
def select_people(self, name=None, lname=None, fname=None, ou=None, email=None,
filter=None) -> List[Mapping]:
"""
search for people records given constraints
:param str|[str] name: a name or partial name or a list of the same to compare against
the first and last names
:param str|[str] lname: a name or partial name a list of the same to compare against
:param str|[str] lname: a name or partial name or a list of the same to compare against
the last names
:param str|[str] fname: a name or partial name a list of the same to compare against
:param str|[str] fname: a name or partial name or a list of the same to compare against
the first names
:param str|[str] email: the email address or a list of the same to compare against people's
email addresses
:param str|int|[str]|[int] ou: an OU or list of OUs, given either as string representing
the OU abbreviation or an integer representing its number,
to compare against people's OU affiliation
Expand All @@ -182,6 +185,8 @@ def select_people(self, name=None, lname=None, fname=None, ou=None, filter=None)
lname = [lname]
if ou is not None and not isinstance(ou, list):
ou = [ou]
if email is not None and not isinstance(email, list):
email = [email]
if filter is None:
filter = {}

Expand All @@ -192,6 +197,8 @@ def select_people(self, name=None, lname=None, fname=None, ou=None, filter=None)
if name:
filter['firstName'] = list(set(filter.get('firstName',{})) | set(name))
filter['lastName'] = list(set(filter.get('lastName', [])) | set(name))
if email:
filter['emailAddress'] = list(set(filter.get('emailAddress', [])) | set(name))

if ou:
filter['ouNumber'] = []
Expand Down
27 changes: 21 additions & 6 deletions python/tests/nistoar/midas/dap/service/test_mds3.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os, json, pdb, logging, tempfile, pathlib
from typing import Mapping
import unittest as test

from nistoar.midas.dbio import inmem, base, AlreadyExists, InvalidUpdate, ObjectNotFound, PartNotAccessible
Expand Down Expand Up @@ -57,6 +58,11 @@ def setUp(self):
# "type": "fsbased",
# "store_dir": os.path.join(tmpdir.name)
"type": "inmem",
},
"default_responsible_org": {
"@type": "org:Organization",
"@id": mds3.NIST_ROR,
"title": "NIST"
}
}
self.dbfact = inmem.InMemoryDBClientFactory({}, { "nextnum": { "mdsy": 2 }})
Expand Down Expand Up @@ -144,10 +150,16 @@ def test_create_record_withdata(self):
self.assertEqual(prec.data['@id'], "ark:/88434/mdsy-0004")
self.assertEqual(prec.data['nonfile_count'], 1)
self.assertNotIn('contactPoint', prec.data)
self.assertTrue(isinstance(prec.data.get('responsibleOrganization'), list))
self.assertEqual(prec.data['responsibleOrganization'][0], 'NIST')
nerd = self.svc._store.open(prec.id)
resmd = nerd.get_res_data()
links = nerd.nonfiles
self.assertEqual(len(links), 1)
self.assertEqual(links.get(0)['accessURL'], prec.meta['softwareLink'])
self.assertTrue(isinstance(resmd.get('responsibleOrganization'), list))
self.assertEqual(len(resmd['responsibleOrganization']), 1)
self.assertEqual(resmd['responsibleOrganization'][0]['title'], 'NIST')


# inject some identity info into service and try again
Expand All @@ -166,7 +178,8 @@ def test_create_record_withdata(self):
self.assertIn('contactPoint', prec.data)
self.assertEqual(prec.data['contactPoint'], {"@type": "vcard:Contact", "fn": "Gurn Cranston",
"hasEmail": "mailto:[email protected]"})

self.assertTrue(isinstance(prec.data.get('responsibleOrganization'), list))
self.assertEqual(prec.data['responsibleOrganization'][0], 'NIST')

def test_moderate_restype(self):
self.create_service()
Expand Down Expand Up @@ -861,14 +874,15 @@ def test_clear_data(self):
pdrid = "ark:/88434/%s-%s" % tuple(prec.id.split(":"))
nerd = self.svc.get_nerdm_data(prec.id)
self.assertEqual(set(nerd.keys()),
{"_schema", "@id", "doi", "_extensionSchemas", "@context", "@type"})
{"_schema", "@id", "doi", "_extensionSchemas", "@context", "@type",
"responsibleOrganization"})

nerd = self.svc.update_data(prec.id,
{"landingPage": "https://example.com",
"contactPoint": { "fn": "Gurn Cranston", "hasEmail": "mailto:[email protected]"}})
self.assertEqual(set(nerd.keys()),
{"_schema", "@id", "doi", "_extensionSchemas", "@context", "@type",
"contactPoint", "landingPage"})
"contactPoint", "landingPage", "responsibleOrganization"})
self.assertEqual(set(nerd["contactPoint"].keys()), {"@type", "fn", "hasEmail"})

with self.assertRaises(PartNotAccessible):
Expand All @@ -878,20 +892,21 @@ def test_clear_data(self):
nerd = self.svc.get_nerdm_data(prec.id)
self.assertEqual(set(nerd.keys()),
{"_schema", "@id", "doi", "_extensionSchemas", "@context", "@type",
"contactPoint", "landingPage"})
"contactPoint", "landingPage", "responsibleOrganization"})
self.assertEqual(set(nerd["contactPoint"].keys()), {"@type", "fn", "hasEmail"})

self.assertIs(self.svc.clear_data(prec.id, "landingPage"), True)
nerd = self.svc.get_nerdm_data(prec.id)
self.assertEqual(set(nerd.keys()),
{"_schema", "@id", "doi", "_extensionSchemas", "@context", "@type",
"contactPoint"})
"contactPoint", "responsibleOrganization"})
self.assertIs(self.svc.clear_data(prec.id, "references"), False)

self.assertIs(self.svc.clear_data(prec.id), True)
nerd = self.svc.get_nerdm_data(prec.id)
self.assertEqual(set(nerd.keys()),
{"_schema", "@id", "doi", "_extensionSchemas", "@context", "@type"})
{"_schema", "@id", "doi", "_extensionSchemas", "@context", "@type",
"responsibleOrganization"})


def test_update(self):
Expand Down

0 comments on commit 220ce2f

Please sign in to comment.