From 570dbfca48ec5e7a231c429899119e21be18007f Mon Sep 17 00:00:00 2001 From: Jason Munro Date: Tue, 2 May 2023 11:09:17 -0700 Subject: [PATCH] Add materials route nesting (#787) * Add materials route nesting * Linting --- mp_api/client/core/settings.py | 9 ++++ mp_api/client/mprester.py | 46 +++++++++++++++++--- mp_api/client/routes/absorption.py | 2 +- mp_api/client/routes/alloys.py | 2 +- mp_api/client/routes/bonds.py | 2 +- mp_api/client/routes/charge_density.py | 2 +- mp_api/client/routes/chemenv.py | 2 +- mp_api/client/routes/dielectric.py | 2 +- mp_api/client/routes/elasticity.py | 2 +- mp_api/client/routes/electrodes.py | 6 +-- mp_api/client/routes/electronic_structure.py | 6 +-- mp_api/client/routes/eos.py | 2 +- mp_api/client/routes/fermi.py | 2 +- mp_api/client/routes/grain_boundary.py | 2 +- mp_api/client/routes/magnetism.py | 2 +- mp_api/client/routes/materials.py | 2 +- mp_api/client/routes/molecules.py | 2 +- mp_api/client/routes/oxidation_states.py | 2 +- mp_api/client/routes/phonon.py | 2 +- mp_api/client/routes/piezo.py | 2 +- mp_api/client/routes/provenance.py | 2 +- mp_api/client/routes/robocrys.py | 2 +- mp_api/client/routes/similarity.py | 2 +- mp_api/client/routes/substrates.py | 2 +- mp_api/client/routes/summary.py | 2 +- mp_api/client/routes/surface_properties.py | 2 +- mp_api/client/routes/synthesis.py | 2 +- mp_api/client/routes/tasks.py | 2 +- mp_api/client/routes/thermo.py | 2 +- mp_api/client/routes/xas.py | 2 +- tests/test_client.py | 30 ++++++------- 31 files changed, 96 insertions(+), 53 deletions(-) diff --git a/mp_api/client/core/settings.py b/mp_api/client/core/settings.py index 68a4e9ec..a858c363 100644 --- a/mp_api/client/core/settings.py +++ b/mp_api/client/core/settings.py @@ -78,5 +78,14 @@ class MAPIClientSettings(BaseSettings): description="Number of characters to use to define the maximum length of a given HTTP URL.", ) + MAX_HTTP_URL_LENGTH: int = Field( + _MAX_HTTP_URL_LENGTH, + description="Number of characters to use to define the maximum length of a given HTTP URL.", + ) + + MIN_EMMET_VERSION: str = Field( + "0.54.0", description="Minimum compatible version of emmet-core for the client." + ) + class Config: env_prefix = "MPRESTER_" diff --git a/mp_api/client/mprester.py b/mp_api/client/mprester.py index 34f03020..e9ce5e76 100644 --- a/mp_api/client/mprester.py +++ b/mp_api/client/mprester.py @@ -10,6 +10,7 @@ from emmet.core.mpid import MPID from emmet.core.settings import EmmetSettings from emmet.core.vasp.calc_types import CalcType +from packaging import version from pymatgen.analysis.phase_diagram import PhaseDiagram from pymatgen.analysis.pourbaix_diagram import IonEntry from pymatgen.core import Element, Structure @@ -20,6 +21,7 @@ from requests import Session, get from mp_api.client.core import BaseRester, MPRestError +from mp_api.client.core.settings import MAPIClientSettings from mp_api.client.core.utils import validate_ids from mp_api.client.routes import ( AbsorptionRester, @@ -64,6 +66,7 @@ ) _EMMET_SETTINGS = EmmetSettings() +_MAPI_SETTINGS = MAPIClientSettings() DEFAULT_API_KEY = environ.get("MP_API_KEY", None) DEFAULT_ENDPOINT = environ.get("MP_API_ENDPOINT", "https://api.materialsproject.org/") @@ -185,6 +188,17 @@ def __init__( self.contribs = None warnings.warn(f"Problem loading MPContribs client: {error}") + # Check if emmet version of server os compatible + emmet_version = version.parse(self.get_emmet_version()) + + if version.parse(emmet_version.base_version) < version.parse( + _MAPI_SETTINGS.MIN_EMMET_VERSION + ): + warnings.warn( + "The installed version of the mp-api client may not be compatible with the API server. " + "Please install a previous version if any problems occur." + ) + self._all_resters = [] if notify_db_version: @@ -208,11 +222,24 @@ def __init__( self._all_resters.append(rester) - setattr( - self, - cls.suffix.replace("/", "_"), # type: ignore - rester, - ) + suffix_split = cls.suffix.split("/") + + if len(suffix_split) == 1 or cls.suffix in [ + "materials/core", + "molecules/core", + ]: + setattr( + self, + cls.suffix.split("/")[0], + rester, + ) + + else: + setattr( + self, + "_".join(suffix_split[1:]), + rester, + ) def __enter__(self): """Support for "with" context.""" @@ -303,6 +330,15 @@ def get_database_version(self): """ return get(url=self.endpoint + "heartbeat").json()["db_version"] + def get_emmet_version(self): + """ + Get the latest version emmet-core and emmet-api used in the + current API service. + + Returns: version as a string + """ + return get(url=self.endpoint + "heartbeat").json()["version"] + def get_material_id_from_task_id(self, task_id: str) -> Union[str, None]: """Returns the current material_id from a given task_id. The material_id should rarely change, and is usually chosen from diff --git a/mp_api/client/routes/absorption.py b/mp_api/client/routes/absorption.py index 4120e1cc..1bdc6120 100644 --- a/mp_api/client/routes/absorption.py +++ b/mp_api/client/routes/absorption.py @@ -8,7 +8,7 @@ class AbsorptionRester(BaseRester[AbsorptionDoc]): - suffix = "absorption" + suffix = "materials/absorption" document_model = AbsorptionDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/alloys.py b/mp_api/client/routes/alloys.py index 2630785c..a566d480 100644 --- a/mp_api/client/routes/alloys.py +++ b/mp_api/client/routes/alloys.py @@ -8,7 +8,7 @@ class AlloysRester(BaseRester[AlloyPairDoc]): - suffix = "alloys" + suffix = "materials/alloys" document_model = AlloyPairDoc # type: ignore primary_key = "pair_id" diff --git a/mp_api/client/routes/bonds.py b/mp_api/client/routes/bonds.py index 54b9f892..19390a57 100644 --- a/mp_api/client/routes/bonds.py +++ b/mp_api/client/routes/bonds.py @@ -9,7 +9,7 @@ class BondsRester(BaseRester[BondingDoc]): - suffix = "bonds" + suffix = "materials/bonds" document_model = BondingDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/charge_density.py b/mp_api/client/routes/charge_density.py index f715fbf6..cf99cc8f 100644 --- a/mp_api/client/routes/charge_density.py +++ b/mp_api/client/routes/charge_density.py @@ -16,7 +16,7 @@ class ChargeDensityRester(BaseRester[ChgcarDataDoc]): - suffix = "charge_density" + suffix = "materials/charge_density" primary_key = "fs_id" document_model = ChgcarDataDoc # type: ignore boto_resource = None diff --git a/mp_api/client/routes/chemenv.py b/mp_api/client/routes/chemenv.py index cf3835d0..f82cae34 100644 --- a/mp_api/client/routes/chemenv.py +++ b/mp_api/client/routes/chemenv.py @@ -8,7 +8,7 @@ class ChemenvRester(BaseRester[ChemEnvDoc]): - suffix = "chemenv" + suffix = "materials/chemenv" document_model = ChemEnvDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/dielectric.py b/mp_api/client/routes/dielectric.py index 2a73d9ca..0334408c 100644 --- a/mp_api/client/routes/dielectric.py +++ b/mp_api/client/routes/dielectric.py @@ -9,7 +9,7 @@ class DielectricRester(BaseRester[DielectricDoc]): - suffix = "dielectric" + suffix = "materials/dielectric" document_model = DielectricDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/elasticity.py b/mp_api/client/routes/elasticity.py index 8386f634..5cf9a5fc 100644 --- a/mp_api/client/routes/elasticity.py +++ b/mp_api/client/routes/elasticity.py @@ -8,7 +8,7 @@ class ElasticityRester(BaseRester[ElasticityDoc]): - suffix = "elasticity" + suffix = "materials/elasticity" document_model = ElasticityDoc # type: ignore primary_key = "task_id" diff --git a/mp_api/client/routes/electrodes.py b/mp_api/client/routes/electrodes.py index 59a848da..940740d5 100644 --- a/mp_api/client/routes/electrodes.py +++ b/mp_api/client/routes/electrodes.py @@ -10,7 +10,7 @@ class ElectrodeRester(BaseRester[InsertionElectrodeDoc]): - suffix = "insertion_electrodes" + suffix = "materials/insertion_electrodes" document_model = InsertionElectrodeDoc # type: ignore primary_key = "battery_id" @@ -113,9 +113,7 @@ def search( # pragma: ignore if isinstance(working_ion, (str, Element)): working_ion = [working_ion] # type: ignore - query_params.update( - {"working_ion": ",".join([str(ele) for ele in working_ion])} # type: ignore - ) + query_params.update({"working_ion": ",".join([str(ele) for ele in working_ion])}) # type: ignore if formula: if isinstance(formula, str): diff --git a/mp_api/client/routes/electronic_structure.py b/mp_api/client/routes/electronic_structure.py index 25042ea8..6fb02fb0 100644 --- a/mp_api/client/routes/electronic_structure.py +++ b/mp_api/client/routes/electronic_structure.py @@ -20,7 +20,7 @@ class ElectronicStructureRester(BaseRester[ElectronicStructureDoc]): - suffix = "electronic_structure" + suffix = "materials/electronic_structure" document_model = ElectronicStructureDoc # type: ignore primary_key = "material_id" @@ -153,7 +153,7 @@ def search( class BandStructureRester(BaseRester): - suffix = "electronic_structure/bandstructure" + suffix = "materials/electronic_structure/bandstructure" document_model = ElectronicStructureDoc # type: ignore def search_bandstructure_summary(self, *args, **kwargs): # pragma: no cover @@ -333,7 +333,7 @@ def get_bandstructure_from_material_id( class DosRester(BaseRester): - suffix = "electronic_structure/dos" + suffix = "materials/electronic_structure/dos" document_model = ElectronicStructureDoc # type: ignore def search_dos_summary(self, *args, **kwargs): # pragma: no cover diff --git a/mp_api/client/routes/eos.py b/mp_api/client/routes/eos.py index f13936fb..73d4b29f 100644 --- a/mp_api/client/routes/eos.py +++ b/mp_api/client/routes/eos.py @@ -8,7 +8,7 @@ class EOSRester(BaseRester[EOSDoc]): - suffix = "eos" + suffix = "materials/eos" document_model = EOSDoc # type: ignore primary_key = "task_id" diff --git a/mp_api/client/routes/fermi.py b/mp_api/client/routes/fermi.py index 1fb2a525..9a5e9b4c 100644 --- a/mp_api/client/routes/fermi.py +++ b/mp_api/client/routes/fermi.py @@ -6,7 +6,7 @@ class FermiRester(BaseRester[FermiDoc]): - suffix = "fermi" + suffix = "materials/fermi" document_model = FermiDoc # type: ignore primary_key = "task_id" diff --git a/mp_api/client/routes/grain_boundary.py b/mp_api/client/routes/grain_boundary.py index 04fe201d..4c136fd1 100644 --- a/mp_api/client/routes/grain_boundary.py +++ b/mp_api/client/routes/grain_boundary.py @@ -9,7 +9,7 @@ class GrainBoundaryRester(BaseRester[GrainBoundaryDoc]): - suffix = "grain_boundary" + suffix = "materials/grain_boundary" document_model = GrainBoundaryDoc # type: ignore primary_key = "task_id" diff --git a/mp_api/client/routes/magnetism.py b/mp_api/client/routes/magnetism.py index f9f8e3c6..96edce5c 100644 --- a/mp_api/client/routes/magnetism.py +++ b/mp_api/client/routes/magnetism.py @@ -10,7 +10,7 @@ class MagnetismRester(BaseRester[MagnetismDoc]): - suffix = "magnetism" + suffix = "materials/magnetism" document_model = MagnetismDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/materials.py b/mp_api/client/routes/materials.py index c7c968ab..5dcb287e 100644 --- a/mp_api/client/routes/materials.py +++ b/mp_api/client/routes/materials.py @@ -13,7 +13,7 @@ class MaterialsRester(BaseRester[MaterialsDoc]): - suffix = "materials" + suffix = "materials/core" document_model = MaterialsDoc # type: ignore supports_versions = True primary_key = "material_id" diff --git a/mp_api/client/routes/molecules.py b/mp_api/client/routes/molecules.py index 6652ad14..acd0b00f 100644 --- a/mp_api/client/routes/molecules.py +++ b/mp_api/client/routes/molecules.py @@ -9,7 +9,7 @@ class MoleculesRester(BaseRester[MoleculesDoc]): - suffix = "molecules" + suffix = "legacy/jcesr" document_model = MoleculesDoc # type: ignore primary_key = "task_id" diff --git a/mp_api/client/routes/oxidation_states.py b/mp_api/client/routes/oxidation_states.py index f8ed4e28..b3a8b25c 100644 --- a/mp_api/client/routes/oxidation_states.py +++ b/mp_api/client/routes/oxidation_states.py @@ -8,7 +8,7 @@ class OxidationStatesRester(BaseRester[OxidationStateDoc]): - suffix = "oxidation_states" + suffix = "materials/oxidation_states" document_model = OxidationStateDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/phonon.py b/mp_api/client/routes/phonon.py index 050c94ca..5b132237 100644 --- a/mp_api/client/routes/phonon.py +++ b/mp_api/client/routes/phonon.py @@ -4,7 +4,7 @@ class PhononRester(BaseRester[PhononBSDOSDoc]): - suffix = "phonon" + suffix = "materials/phonon" document_model = PhononBSDOSDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/piezo.py b/mp_api/client/routes/piezo.py index e41c13ba..5c90205e 100644 --- a/mp_api/client/routes/piezo.py +++ b/mp_api/client/routes/piezo.py @@ -9,7 +9,7 @@ class PiezoRester(BaseRester[PiezoelectricDoc]): - suffix = "piezoelectric" + suffix = "materials/piezoelectric" document_model = PiezoelectricDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/provenance.py b/mp_api/client/routes/provenance.py index 1eb78a47..3eb3cfc7 100644 --- a/mp_api/client/routes/provenance.py +++ b/mp_api/client/routes/provenance.py @@ -7,7 +7,7 @@ class ProvenanceRester(BaseRester[ProvenanceDoc]): - suffix = "provenance" + suffix = "materials/provenance" document_model = ProvenanceDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/robocrys.py b/mp_api/client/routes/robocrys.py index 980f4285..00f41077 100644 --- a/mp_api/client/routes/robocrys.py +++ b/mp_api/client/routes/robocrys.py @@ -7,7 +7,7 @@ class RobocrysRester(BaseRester[RobocrystallogapherDoc]): - suffix = "robocrys" + suffix = "materials/robocrys" document_model = RobocrystallogapherDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/similarity.py b/mp_api/client/routes/similarity.py index f8d8152b..290365c5 100644 --- a/mp_api/client/routes/similarity.py +++ b/mp_api/client/routes/similarity.py @@ -4,7 +4,7 @@ class SimilarityRester(BaseRester[SimilarityDoc]): - suffix = "similarity" + suffix = "materials/similarity" document_model = SimilarityDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/substrates.py b/mp_api/client/routes/substrates.py index d7e78b73..c3196fb6 100644 --- a/mp_api/client/routes/substrates.py +++ b/mp_api/client/routes/substrates.py @@ -8,7 +8,7 @@ class SubstratesRester(BaseRester[SubstratesDoc]): - suffix = "substrates" + suffix = "materials/substrates" document_model = SubstratesDoc # type: ignore primary_key = "film_id" diff --git a/mp_api/client/routes/summary.py b/mp_api/client/routes/summary.py index 5d6a181a..cbd868c0 100644 --- a/mp_api/client/routes/summary.py +++ b/mp_api/client/routes/summary.py @@ -12,7 +12,7 @@ class SummaryRester(BaseRester[SummaryDoc]): - suffix = "summary" + suffix = "materials/summary" document_model = SummaryDoc # type: ignore primary_key = "material_id" diff --git a/mp_api/client/routes/surface_properties.py b/mp_api/client/routes/surface_properties.py index 462101b0..b1a99ad2 100644 --- a/mp_api/client/routes/surface_properties.py +++ b/mp_api/client/routes/surface_properties.py @@ -8,7 +8,7 @@ class SurfacePropertiesRester(BaseRester[SurfacePropDoc]): - suffix = "surface_properties" + suffix = "materials/surface_properties" document_model = SurfacePropDoc # type: ignore primary_key = "task_id" diff --git a/mp_api/client/routes/synthesis.py b/mp_api/client/routes/synthesis.py index bdd7f0fd..ac4b8beb 100644 --- a/mp_api/client/routes/synthesis.py +++ b/mp_api/client/routes/synthesis.py @@ -11,7 +11,7 @@ class SynthesisRester(BaseRester[SynthesisSearchResultModel]): - suffix = "synthesis" + suffix = "materials/synthesis" document_model = SynthesisSearchResultModel # type: ignore def search_synthesis_text(self, *args, **kwargs): # pragma: no cover diff --git a/mp_api/client/routes/tasks.py b/mp_api/client/routes/tasks.py index 1312e18a..6828d8cd 100644 --- a/mp_api/client/routes/tasks.py +++ b/mp_api/client/routes/tasks.py @@ -8,7 +8,7 @@ class TaskRester(BaseRester[TaskDoc]): - suffix = "tasks" + suffix = "materials/tasks" document_model = TaskDoc # type: ignore primary_key = "task_id" diff --git a/mp_api/client/routes/thermo.py b/mp_api/client/routes/thermo.py index 0ab2c0b3..000e1433 100644 --- a/mp_api/client/routes/thermo.py +++ b/mp_api/client/routes/thermo.py @@ -12,7 +12,7 @@ class ThermoRester(BaseRester[ThermoDoc]): - suffix = "thermo" + suffix = "materials/thermo" document_model = ThermoDoc # type: ignore supports_versions = True primary_key = "thermo_id" diff --git a/mp_api/client/routes/xas.py b/mp_api/client/routes/xas.py index 2c77a2cd..a5b202eb 100644 --- a/mp_api/client/routes/xas.py +++ b/mp_api/client/routes/xas.py @@ -9,7 +9,7 @@ class XASRester(BaseRester[XASDoc]): - suffix = "xas" + suffix = "materials/xas" document_model = XASDoc # type: ignore primary_key = "spectrum_id" diff --git a/tests/test_client.py b/tests/test_client.py index bc6fc2f7..67371eb0 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -7,25 +7,25 @@ # -- Rester name data for generic tests key_only_resters = { - "phonon": "mp-11703", - "similarity": "mp-149", + "materials_phonon": "mp-11703", + "materials_similarity": "mp-149", "doi": "mp-149", - "wulff": "mp-149", - "charge_density": "mp-1936745", - "provenance": "mp-149", - "robocrys": "mp-1025395", + "materials_wulff": "mp-149", + "materials_charge_density": "mp-1936745", + "materials_provenance": "mp-149", + "materials_robocrys": "mp-1025395", } search_only_resters = [ - "grain_boundary", - "electronic_structure_bandstructure", - "electronic_structure_dos", - "substrates", - "synthesis", + "materials_grain_boundary", + "materials_electronic_structure_bandstructure", + "materials_electronic_structure_dos", + "materials_substrates", + "materials_synthesis", ] special_resters = [ - "charge_density", + "materials_charge_density", ] ignore_generic = [ @@ -33,9 +33,9 @@ "_general_store", # "tasks", # "bonds", - "xas", - "elasticity", - "fermi", + "materials_xas", + "materials_elasticity", + "materials_fermi", # "alloys", # "summary", ] # temp