diff --git a/LICENSE b/LICENSE index 2ae1bd118..ee5ed1792 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -maggma Copyright (c) 2017, The Regents of the University of +Copyright (c) 2017, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved. diff --git a/mp_api/client/core/client.py b/mp_api/client/core/client.py index 3ee0b32e4..34873b089 100644 --- a/mp_api/client/core/client.py +++ b/mp_api/client/core/client.py @@ -7,7 +7,6 @@ import inspect import itertools -import json import os import platform import sys @@ -29,9 +28,7 @@ from urllib.parse import quote, urljoin import requests -from bson import json_util from emmet.core.utils import jsanitize -from monty.json import MontyDecoder from pydantic import BaseModel, create_model from requests.adapters import HTTPAdapter from requests.exceptions import RequestException @@ -40,7 +37,7 @@ from urllib3.util.retry import Retry from mp_api.client.core.settings import MAPIClientSettings -from mp_api.client.core.utils import api_sanitize, validate_ids +from mp_api.client.core.utils import api_sanitize, load_json, validate_ids try: import boto3 @@ -270,11 +267,7 @@ def _post_resource( response = self.session.post(url, json=payload, verify=True, params=params) if response.status_code == 200: - if self.monty_decode: - data = json.loads(response.text, cls=MontyDecoder) - else: - data = json.loads(response.text) - + data = load_json(response.text, deser=self.monty_decode) if self.document_model and use_document_model: if isinstance(data["data"], dict): data["data"] = self.document_model.model_validate(data["data"]) # type: ignore @@ -287,7 +280,7 @@ def _post_resource( else: try: - data = json.loads(response.text)["detail"] + data = load_json(response.text)["detail"] except (JSONDecodeError, KeyError): data = f"Response {response.text}" if isinstance(data, str): @@ -342,11 +335,7 @@ def _patch_resource( response = self.session.patch(url, json=payload, verify=True, params=params) if response.status_code == 200: - if self.monty_decode: - data = json.loads(response.text, cls=MontyDecoder) - else: - data = json.loads(response.text) - + data = load_json(response.text, deser=self.monty_decode) if self.document_model and use_document_model: if isinstance(data["data"], dict): data["data"] = self.document_model.model_validate(data["data"]) # type: ignore @@ -359,7 +348,7 @@ def _patch_resource( else: try: - data = json.loads(response.text)["detail"] + data = load_json(response.text)["detail"] except (JSONDecodeError, KeyError): data = f"Response {response.text}" if isinstance(data, str): @@ -384,18 +373,24 @@ def _query_open_data( self, bucket: str, key: str, - decoder: Callable, + decoder: Callable | None = None, ) -> tuple[list[dict] | list[bytes], int]: """Query and deserialize Materials Project AWS open data s3 buckets. Args: bucket (str): Materials project bucket name key (str): Key for file including all prefixes - decoder(Callable): Callable used to deserialize data + decoder(Callable or None): Callable used to deserialize data. + Defaults to mp_api.core.utils.load_json Returns: dict: MontyDecoded data """ + if not decoder: + + def decoder(x): + return load_json(x, deser=self.monty_decode) + file = open( f"s3://{bucket}/{key}", encoding="utf-8", @@ -527,16 +522,11 @@ def _query_resource( "Ignoring `fields` argument: All fields are always included when no query is provided." ) - decoder = ( - MontyDecoder().decode if self.monty_decode else json_util.loads - ) - # Multithreaded function inputs s3_params_list = { key: { "bucket": bucket, "key": key, - "decoder": decoder, } for key in keys } @@ -1013,11 +1003,7 @@ def _submit_request_and_process( ) if response.status_code == 200: - if self.monty_decode: - data = json.loads(response.text, cls=MontyDecoder) - else: - data = json.loads(response.text) - + data = load_json(response.text, deser=self.monty_decode) # other sub-urls may use different document models # the client does not handle this in a particularly smart way currently if self.document_model and use_document_model: @@ -1029,7 +1015,7 @@ def _submit_request_and_process( else: try: - data = json.loads(response.text)["detail"] + data = load_json(response.text)["detail"] except (JSONDecodeError, KeyError): data = f"Response {response.text}" if isinstance(data, str): diff --git a/mp_api/client/core/utils.py b/mp_api/client/core/utils.py index 1bba91954..67b6f5a81 100644 --- a/mp_api/client/core/utils.py +++ b/mp_api/client/core/utils.py @@ -4,8 +4,9 @@ from functools import cache from typing import Optional, get_args -from maggma.utils import get_flat_models_from_model -from monty.json import MSONable +import orjson +from emmet.core.utils import get_flat_models_from_model +from monty.json import MontyDecoder, MSONable from pydantic import BaseModel from pydantic._internal._utils import lenient_issubclass from pydantic.fields import FieldInfo @@ -13,6 +14,14 @@ from mp_api.client.core.settings import MAPIClientSettings +def load_json(json_like: str | bytes, deser: bool = False, encoding: str = "utf-8"): + """Utility to load json in consistent manner.""" + data = orjson.loads( + json_like if isinstance(json_like, bytes) else json_like.encode(encoding) + ) + return MontyDecoder().process_decoded(data) if deser else data + + def validate_ids(id_list: list[str]): """Function to validate material and task IDs. diff --git a/mp_api/client/mprester.py b/mp_api/client/mprester.py index 38ca8c11a..f14d2d0b2 100644 --- a/mp_api/client/mprester.py +++ b/mp_api/client/mprester.py @@ -1,11 +1,9 @@ from __future__ import annotations import itertools -import json import os import warnings from functools import cache, lru_cache -from json import loads from typing import TYPE_CHECKING from emmet.core.electronic_structure import BSPathType @@ -14,7 +12,6 @@ from emmet.core.tasks import TaskDoc from emmet.core.thermo import ThermoType from emmet.core.vasp.calc_types import CalcType -from monty.json import MontyDecoder from packaging import version from pymatgen.analysis.phase_diagram import PhaseDiagram from pymatgen.analysis.pourbaix_diagram import IonEntry @@ -27,7 +24,7 @@ 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.core.utils import load_json, validate_ids from mp_api.client.routes import GeneralStoreRester, MessagesRester, UserSettingsRester from mp_api.client.routes.materials import ( AbsorptionRester, @@ -35,6 +32,7 @@ BandStructureRester, BondsRester, ChemenvRester, + ConversionElectrodeRester, DielectricRester, DOIRester, DosRester, @@ -99,6 +97,7 @@ class MPRester: robocrys: RobocrysRester synthesis: SynthesisRester insertion_electrodes: ElectrodeRester + conversion_electrodes: ConversionElectrodeRester electronic_structure: ElectronicStructureRester electronic_structure_bandstructure: BandStructureRester electronic_structure_dos: DosRester @@ -1338,11 +1337,10 @@ def get_charge_density_from_task_id( Returns: (Chgcar, (Chgcar, TaskDoc | dict), None): Pymatgen Chgcar object, or tuple with object and TaskDoc """ - decoder = MontyDecoder().decode if self.monty_decode else json.loads kwargs = dict( bucket="materialsproject-parsed", key=f"chgcars/{str(task_id)}.json.gz", - decoder=decoder, + decoder=lambda x: load_json(x, deser=self.monty_decode), ) chgcar = self.materials.tasks._query_open_data(**kwargs)[0] if not chgcar: @@ -1476,7 +1474,7 @@ def _check_nomad_exist(url) -> bool: response = get(url=url) if response.status_code != 200: return False - content = loads(response.text) + content = load_json(response.text) if content["pagination"]["total"] == 0: return False return True diff --git a/mp_api/client/routes/materials/__init__.py b/mp_api/client/routes/materials/__init__.py index 382fb8f63..63fd0e3bd 100644 --- a/mp_api/client/routes/materials/__init__.py +++ b/mp_api/client/routes/materials/__init__.py @@ -6,7 +6,7 @@ from .dielectric import DielectricRester from .doi import DOIRester from .elasticity import ElasticityRester -from .electrodes import ElectrodeRester +from .electrodes import ConversionElectrodeRester, ElectrodeRester from .electronic_structure import ( BandStructureRester, DosRester, diff --git a/mp_api/client/routes/materials/electrodes.py b/mp_api/client/routes/materials/electrodes.py index 4738b3ea5..99ee01657 100644 --- a/mp_api/client/routes/materials/electrodes.py +++ b/mp_api/client/routes/materials/electrodes.py @@ -1,18 +1,18 @@ from __future__ import annotations +import warnings from collections import defaultdict -from emmet.core.electrode import InsertionElectrodeDoc +from emmet.core.electrode import ConversionElectrodeDoc, InsertionElectrodeDoc from pymatgen.core.periodic_table import Element from mp_api.client.core import BaseRester from mp_api.client.core.utils import validate_ids -class ElectrodeRester(BaseRester[InsertionElectrodeDoc]): - suffix = "materials/insertion_electrodes" - document_model = InsertionElectrodeDoc # type: ignore +class BaseElectrodeRester(BaseRester): primary_key = "battery_id" + _exclude_search_fields: list[str] | None = None def search( # pragma: ignore self, @@ -38,7 +38,7 @@ def search( # pragma: ignore chunk_size: int = 1000, all_fields: bool = True, fields: list[str] | None = None, - ) -> list[InsertionElectrodeDoc] | list[dict]: + ) -> list[InsertionElectrodeDoc | ConversionElectrodeDoc] | list[dict]: """Query using a variety of search criteria. Arguments: @@ -74,11 +74,11 @@ def search( # pragma: ignore num_chunks (int): Maximum number of chunks of data to yield. None will yield all possible. chunk_size (int): Number of data entries per chunk. all_fields (bool): Whether to return all fields in the document. Defaults to True. - fields (List[str]): List of fields in InsertionElectrodeDoc to return data for. + fields (List[str]): List of fields in InsertionElectrodeDoc or ConversionElectrodeDoc to return data for. Default is battery_id and last_updated if all_fields is False. Returns: - ([InsertionElectrodeDoc], [dict]) List of insertion electrode documents or dictionaries. + ([InsertionElectrodeDoc or ConversionElectrodeDoc], [dict]) List of insertion/conversion electrode documents or dictionaries. """ query_params = defaultdict(dict) # type: dict @@ -134,6 +134,17 @@ def search( # pragma: ignore else: query_params.update({param: value}) + excluded_fields = self._exclude_search_fields or [] + ignored_fields = { + entry + for entry in excluded_fields + if query_params.pop(entry, None) is not None + } + if ignored_fields: + warnings.warn( + f"Ignoring fields {', '.join(ignored_fields)} which are not valid options for {self.__class__.__name__}" + ) + query_params = { entry: query_params[entry] for entry in query_params @@ -141,3 +152,25 @@ def search( # pragma: ignore } return super()._search(**query_params) + + +class ElectrodeRester(BaseElectrodeRester): + """Search insertion electrode documents.""" + + suffix = "materials/insertion_electrodes" + document_model = InsertionElectrodeDoc # type: ignore + + +class ConversionElectrodeRester(BaseElectrodeRester): + """Search conversion electrode documents.""" + + suffix = "materials/conversion_electrodes" + document_model = ConversionElectrodeDoc # type: ignore + # TODO: formula, chemsys, and elements do not appear to work in the API + _exclude_search_fields = [ + "formula", + "chemsys", + "elements", + "stability_charge", + "stability_discharge", + ] diff --git a/mp_api/client/routes/materials/electronic_structure.py b/mp_api/client/routes/materials/electronic_structure.py index d7284712e..2fd487d07 100644 --- a/mp_api/client/routes/materials/electronic_structure.py +++ b/mp_api/client/routes/materials/electronic_structure.py @@ -1,6 +1,5 @@ from __future__ import annotations -import json import warnings from collections import defaultdict @@ -9,7 +8,6 @@ DOSProjectionType, ElectronicStructureDoc, ) -from monty.json import MontyDecoder from pymatgen.analysis.magnetism.analyzer import Ordering from pymatgen.core.periodic_table import Element from pymatgen.electronic_structure.core import OrbitalType, Spin @@ -234,11 +232,9 @@ def get_bandstructure_from_task_id(self, task_id: str): Returns: bandstructure (BandStructure): BandStructure or BandStructureSymmLine object """ - decoder = MontyDecoder().decode if self.monty_decode else json.loads result = self._query_open_data( bucket="materialsproject-parsed", key=f"bandstructures/{task_id}.json.gz", - decoder=decoder, )[0] if result: @@ -430,11 +426,9 @@ def get_dos_from_task_id(self, task_id: str): Returns: bandstructure (CompleteDos): CompleteDos object """ - decoder = MontyDecoder().decode if self.monty_decode else json.loads result = self._query_open_data( bucket="materialsproject-parsed", key=f"dos/{task_id}.json.gz", - decoder=decoder, )[0] if result: diff --git a/mp_api/client/routes/materials/materials.py b/mp_api/client/routes/materials/materials.py index 1e8f0157a..a7bff5c46 100644 --- a/mp_api/client/routes/materials/materials.py +++ b/mp_api/client/routes/materials/materials.py @@ -13,6 +13,7 @@ BandStructureRester, BondsRester, ChemenvRester, + ConversionElectrodeRester, DielectricRester, DosRester, ElasticityRester, @@ -62,6 +63,7 @@ class MaterialsRester(BaseRester[MaterialsDoc]): "robocrys", "synthesis", "insertion_electrodes", + "conversion_electrodes", "electronic_structure", "electronic_structure_bandstructure", "electronic_structure_dos", @@ -92,6 +94,7 @@ class MaterialsRester(BaseRester[MaterialsDoc]): robocrys: RobocrysRester synthesis: SynthesisRester insertion_electrodes: ElectrodeRester + conversion_electrodes: ConversionElectrodeRester electronic_structure: ElectronicStructureRester electronic_structure_bandstructure: BandStructureRester electronic_structure_dos: DosRester diff --git a/mp_api/client/routes/materials/phonon.py b/mp_api/client/routes/materials/phonon.py index 4ce52950e..605591048 100644 --- a/mp_api/client/routes/materials/phonon.py +++ b/mp_api/client/routes/materials/phonon.py @@ -1,11 +1,9 @@ from __future__ import annotations -import json from collections import defaultdict import numpy as np from emmet.core.phonon import PhononBS, PhononBSDOSDoc, PhononDOS -from monty.json import MontyDecoder from mp_api.client.core import BaseRester, MPRestError from mp_api.client.core.utils import validate_ids @@ -75,11 +73,9 @@ def get_bandstructure_from_material_id(self, material_id: str, phonon_method: st Returns: bandstructure (PhononBS): PhononBS object """ - decoder = MontyDecoder().decode if self.monty_decode else json.loads result = self._query_open_data( bucket="materialsproject-parsed", key=f"ph-bandstructures/{phonon_method}/{material_id}.json.gz", - decoder=decoder, )[0] if not result or not result[0]: @@ -100,11 +96,9 @@ def get_dos_from_material_id(self, material_id: str, phonon_method: str): Returns: dos (PhononDOS): PhononDOS object """ - decoder = MontyDecoder().decode if self.monty_decode else json.loads result = self._query_open_data( bucket="materialsproject-parsed", key=f"ph-dos/{phonon_method}/{material_id}.json.gz", - decoder=decoder, )[0] if not result or not result[0]: @@ -124,11 +118,9 @@ def get_forceconstants_from_material_id(self, material_id: str): Returns: force constants (list[list[Matrix3D]]): PhononDOS object """ - decoder = MontyDecoder().decode if self.monty_decode else json.loads result = self._query_open_data( bucket="materialsproject-parsed", key=f"ph-force-constants/{material_id}.json.gz", - decoder=decoder, )[0] if not result or not result[0]: diff --git a/mp_api/client/routes/materials/robocrys.py b/mp_api/client/routes/materials/robocrys.py index 829a78253..bf24ccc65 100644 --- a/mp_api/client/routes/materials/robocrys.py +++ b/mp_api/client/routes/materials/robocrys.py @@ -32,7 +32,7 @@ def search( robocrys_docs = self._query_resource( criteria={"keywords": keyword_string, "_limit": chunk_size}, suburl="text_search", - use_document_model=True, + use_document_model=self.use_document_model, chunk_size=chunk_size, num_chunks=num_chunks, ).get("data", None) diff --git a/mp_api/client/routes/materials/thermo.py b/mp_api/client/routes/materials/thermo.py index 4473900de..ae19a4900 100644 --- a/mp_api/client/routes/materials/thermo.py +++ b/mp_api/client/routes/materials/thermo.py @@ -4,12 +4,11 @@ import numpy as np from emmet.core.thermo import ThermoDoc, ThermoType -from monty.json import MontyDecoder from pymatgen.analysis.phase_diagram import PhaseDiagram from pymatgen.core import Element from mp_api.client.core import BaseRester -from mp_api.client.core.utils import validate_ids +from mp_api.client.core.utils import load_json, validate_ids class ThermoRester(BaseRester[ThermoDoc]): @@ -170,7 +169,7 @@ def get_phase_diagram_from_chemsys( pd = self._query_open_data( bucket="materialsproject-build", key=obj_key, - decoder=MontyDecoder().decode, + decoder=lambda x: load_json(x, deser=True), )[0][0].get("phase_diagram") # Ensure el_ref keys are Element objects for PDPlotter. diff --git a/pyproject.toml b/pyproject.toml index fc3450c66..33c343134 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,6 @@ classifiers = [ ] dependencies = [ "msgpack", - "maggma>=0.57.1", "pymatgen>=2022.3.7,!=2024.2.20", "typing-extensions>=3.7.4.1", "requests>=2.23.0", diff --git a/requirements/requirements-ubuntu-latest_py3.11.txt b/requirements/requirements-ubuntu-latest_py3.11.txt index dcd8c18ef..754502940 100644 --- a/requirements/requirements-ubuntu-latest_py3.11.txt +++ b/requirements/requirements-ubuntu-latest_py3.11.txt @@ -4,93 +4,48 @@ # # pip-compile --cert=None --client-cert=None --index-url=None --output-file=requirements/requirements-ubuntu-latest_py3.11.txt --pip-args=None pyproject.toml # -aioitertools==0.12.0 - # via maggma annotated-types==0.7.0 # via pydantic -attrs==25.3.0 - # via - # jsonlines - # jsonschema - # referencing -bcrypt==5.0.0 - # via paramiko bibtexparser==1.4.3 # via pymatgen -boto3==1.40.40 - # via maggma -botocore==1.40.40 - # via - # boto3 - # s3transfer certifi==2025.8.3 # via requests -cffi==2.0.0 - # via - # cryptography - # pynacl charset-normalizer==3.4.3 # via requests contourpy==1.3.3 # via matplotlib -cryptography==46.0.1 - # via paramiko cycler==0.12.1 # via matplotlib -dnspython==2.8.0 - # via - # maggma - # pymongo emmet-core==0.84.10 # via mp-api (pyproject.toml) -fonttools==4.60.0 +fonttools==4.60.1 # via matplotlib idna==3.10 # via requests -invoke==2.2.0 - # via paramiko -jmespath==1.0.1 - # via - # boto3 - # botocore joblib==1.5.2 # via pymatgen -jsonlines==4.0.0 - # via maggma -jsonschema==4.25.1 - # via maggma -jsonschema-specifications==2025.9.1 - # via jsonschema kiwisolver==1.4.9 # via matplotlib latexcodec==3.0.1 # via pybtex -maggma==0.72.0 - # via mp-api (pyproject.toml) matplotlib==3.10.6 # via pymatgen -mongomock==4.3.0 - # via maggma monty==2025.3.3 # via # emmet-core - # maggma # mp-api (pyproject.toml) # pymatgen mpmath==1.3.0 # via sympy msgpack==1.1.1 - # via - # maggma - # mp-api (pyproject.toml) -narwhals==2.5.0 + # via mp-api (pyproject.toml) +narwhals==2.6.0 # via plotly networkx==3.5 # via pymatgen numpy==2.3.3 # via # contourpy - # maggma # matplotlib # monty # pandas @@ -99,34 +54,24 @@ numpy==2.3.3 # scipy # spglib orjson==3.11.3 - # via - # maggma - # pymatgen + # via pymatgen packaging==25.0 # via # matplotlib - # mongomock # plotly palettable==3.3.3 # via pymatgen -pandas==2.3.2 - # via - # maggma - # pymatgen -paramiko==4.0.0 - # via sshtunnel +pandas==2.3.3 + # via pymatgen pillow==11.3.0 # via matplotlib plotly==6.3.0 # via pymatgen pybtex==0.25.1 # via emmet-core -pycparser==2.23 - # via cffi pydantic==2.11.9 # via # emmet-core - # maggma # pydantic-settings # pymatgen-io-validation pydantic-core==2.33.2 @@ -134,10 +79,7 @@ pydantic-core==2.33.2 pydantic-settings==2.11.0 # via # emmet-core - # maggma # pymatgen-io-validation -pydash==8.0.5 - # via maggma pymatgen==2025.6.14 # via # emmet-core @@ -145,83 +87,54 @@ pymatgen==2025.6.14 # pymatgen-io-validation pymatgen-io-validation==0.1.2 # via emmet-core -pymongo==4.10.1 - # via maggma -pynacl==1.6.0 - # via paramiko pyparsing==3.2.5 # via # bibtexparser # matplotlib python-dateutil==2.9.0.post0 # via - # botocore - # maggma # matplotlib # pandas python-dotenv==1.1.1 # via pydantic-settings pytz==2025.2 - # via - # mongomock - # pandas + # via pandas pyyaml==6.0.3 # via pybtex -pyzmq==27.1.0 - # via maggma -referencing==0.36.2 - # via - # jsonschema - # jsonschema-specifications requests==2.32.5 # via # mp-api (pyproject.toml) # pymatgen # pymatgen-io-validation -rpds-py==0.27.1 - # via - # jsonschema - # referencing ruamel-yaml==0.18.15 # via - # maggma # monty # pymatgen ruamel-yaml-clib==0.2.14 # via ruamel-yaml -s3transfer==0.14.0 - # via boto3 scipy==1.16.2 # via pymatgen -sentinels==1.1.1 - # via mongomock six==1.17.0 # via python-dateutil smart-open==7.3.1 # via mp-api (pyproject.toml) spglib==2.6.0 # via pymatgen -sshtunnel==0.4.0 - # via maggma sympy==1.14.0 # via pymatgen tabulate==0.9.0 # via pymatgen tqdm==4.67.1 - # via - # maggma - # pymatgen + # via pymatgen typing-extensions==4.15.0 # via # emmet-core # mp-api (pyproject.toml) # pydantic # pydantic-core - # pydash - # referencing # spglib # typing-inspection -typing-inspection==0.4.1 +typing-inspection==0.4.2 # via # pydantic # pydantic-settings @@ -230,11 +143,6 @@ tzdata==2025.2 uncertainties==3.2.3 # via pymatgen urllib3==2.5.0 - # via - # botocore - # requests + # via requests wrapt==1.17.3 # via smart-open - -# The following packages are considered to be unsafe in a requirements file: -# setuptools diff --git a/requirements/requirements-ubuntu-latest_py3.11_extras.txt b/requirements/requirements-ubuntu-latest_py3.11_extras.txt index 868c9b155..7e348baef 100644 --- a/requirements/requirements-ubuntu-latest_py3.11_extras.txt +++ b/requirements/requirements-ubuntu-latest_py3.11_extras.txt @@ -4,8 +4,6 @@ # # pip-compile --all-extras --cert=None --client-cert=None --index-url=None --output-file=requirements/requirements-ubuntu-latest_py3.11_extras.txt --pip-args=None pyproject.toml # -aioitertools==0.12.0 - # via maggma alabaster==1.0.0 # via sphinx annotated-types==0.7.0 @@ -18,22 +16,17 @@ asttokens==3.0.0 # via stack-data attrs==25.3.0 # via - # jsonlines # jsonschema # referencing babel==2.17.0 # via sphinx -bcrypt==5.0.0 - # via paramiko bibtexparser==1.4.3 # via pymatgen boltons==25.0.0 # via mpcontribs-client -boto3==1.40.40 - # via - # maggma - # mp-api (pyproject.toml) -botocore==1.40.40 +boto3==1.40.43 + # via mp-api (pyproject.toml) +botocore==1.40.43 # via # boto3 # s3transfer @@ -45,10 +38,6 @@ cachetools==6.2.0 # via mpcontribs-client certifi==2025.8.3 # via requests -cffi==2.0.0 - # via - # cryptography - # pynacl cfgv==3.4.0 # via pre-commit charset-normalizer==3.4.3 @@ -57,8 +46,6 @@ contourpy==1.3.3 # via matplotlib coverage[toml]==7.10.7 # via pytest-cov -cryptography==46.0.1 - # via paramiko custodian==2025.8.13 # via mp-api (pyproject.toml) cycler==0.12.1 @@ -69,7 +56,6 @@ distlib==0.4.0 # via virtualenv dnspython==2.8.0 # via - # maggma # pyisemail # pymongo docutils==0.21.2 @@ -94,7 +80,7 @@ flexcache==0.3 # via pint flexparser==0.4 # via pint -fonttools==4.60.0 +fonttools==4.60.1 # via matplotlib fqdn==1.5.1 # via jsonschema @@ -116,9 +102,7 @@ inflect==7.5.0 # via robocrys iniconfig==2.1.0 # via pytest -invoke==2.2.0 - # via paramiko -ipython==9.5.0 +ipython==9.6.0 # via mpcontribs-client ipython-pygments-lexers==1.1.1 # via ipython @@ -140,8 +124,6 @@ joblib==1.5.2 # scikit-learn json2html==1.3.0 # via mpcontribs-client -jsonlines==4.0.0 - # via maggma jsonpointer==3.0.0 # via jsonschema jsonref==1.1.0 @@ -149,7 +131,6 @@ jsonref==1.1.0 jsonschema[format-nongpl]==4.25.1 # via # bravado-core - # maggma # swagger-spec-validator jsonschema-specifications==2025.9.1 # via jsonschema @@ -161,9 +142,7 @@ latexcodec==3.0.1 # via pybtex lazy-loader==0.4 # via scikit-image -maggma==0.72.0 - # via mp-api (pyproject.toml) -markupsafe==3.0.2 +markupsafe==3.0.3 # via jinja2 matminer==0.9.3 # via robocrys @@ -187,15 +166,12 @@ mdanalysis==2.9.0 # transport-analysis mmtf-python==1.1.3 # via mdanalysis -mongomock==4.3.0 - # via maggma monotonic==1.6 # via bravado monty==2025.3.3 # via # custodian # emmet-core - # maggma # matminer # mp-api (pyproject.toml) # pymatgen @@ -214,7 +190,6 @@ msgpack==1.1.1 # via # bravado # bravado-core - # maggma # mmtf-python # mp-api (pyproject.toml) mypy==1.18.2 @@ -223,7 +198,7 @@ mypy-extensions==1.1.0 # via # mp-api (pyproject.toml) # mypy -narwhals==2.5.0 +narwhals==2.6.0 # via plotly networkx==3.5 # via @@ -238,7 +213,6 @@ numpy==1.26.4 # contourpy # griddataformats # imageio - # maggma # matminer # matplotlib # mdanalysis @@ -265,15 +239,12 @@ numpy==1.26.4 # tidynamics # tifffile orjson==3.11.3 - # via - # maggma - # pymatgen + # via pymatgen packaging==25.0 # via # lazy-loader # matplotlib # mdanalysis - # mongomock # plotly # pytest # scikit-image @@ -281,17 +252,14 @@ packaging==25.0 # statsmodels palettable==3.3.3 # via pymatgen -pandas==2.3.2 +pandas==2.3.3 # via - # maggma # matminer # mpcontribs-client # pymatgen # seaborn # solvation-analysis # statsmodels -paramiko==4.0.0 - # via sshtunnel parso==0.8.5 # via jedi pathspec==0.12.1 @@ -343,12 +311,9 @@ pycodestyle==2.14.0 # via # flake8 # mp-api (pyproject.toml) -pycparser==2.23 - # via cffi pydantic==2.11.9 # via # emmet-core - # maggma # pydantic-settings # pymatgen-io-validation pydantic-core==2.33.2 @@ -356,10 +321,7 @@ pydantic-core==2.33.2 pydantic-settings==2.11.0 # via # emmet-core - # maggma # pymatgen-io-validation -pydash==8.0.5 - # via maggma pyflakes==3.4.0 # via flake8 pygments==2.19.2 @@ -390,13 +352,10 @@ pymatgen-analysis-diffusion==2024.7.15 # via emmet-core pymatgen-io-validation==0.1.2 # via emmet-core -pymongo==4.10.1 +pymongo==4.15.2 # via - # maggma # matminer # mpcontribs-client -pynacl==1.6.0 - # via paramiko pyparsing==3.2.5 # via # bibtexparser @@ -423,7 +382,6 @@ python-dateutil==2.9.0.post0 # botocore # bravado # bravado-core - # maggma # matplotlib # pandas python-dotenv==1.1.1 @@ -431,7 +389,6 @@ python-dotenv==1.1.1 pytz==2025.2 # via # bravado-core - # mongomock # pandas pyyaml==6.0.3 # via @@ -440,8 +397,6 @@ pyyaml==6.0.3 # pre-commit # pybtex # swagger-spec-validator -pyzmq==27.1.0 - # via maggma rdkit==2025.3.6 # via solvation-analysis referencing==0.36.2 @@ -477,7 +432,6 @@ rpds-py==0.27.1 ruamel-yaml==0.18.15 # via # custodian - # maggma # monty # pymatgen # robocrys @@ -506,8 +460,6 @@ seekpath==2.1.0 # via emmet-core semantic-version==2.10.0 # via mpcontribs-client -sentinels==1.1.1 - # via mongomock shapely==2.1.2 # via pymatgen-analysis-alloys simplejson==3.20.2 @@ -546,8 +498,6 @@ sphinxcontrib-qthelp==2.0.0 # via sphinx sphinxcontrib-serializinghtml==2.0.0 # via sphinx -sshtunnel==0.4.0 - # via maggma stack-data==0.6.3 # via ipython statsmodels==0.14.5 @@ -568,11 +518,10 @@ threadpoolctl==3.6.0 # scikit-learn tidynamics==1.1.2 # via transport-analysis -tifffile==2025.9.20 +tifffile==2025.9.30 # via scikit-image tqdm==4.67.1 # via - # maggma # matminer # mdanalysis # mpcontribs-client @@ -603,14 +552,13 @@ typing-extensions==4.15.0 # pint # pydantic # pydantic-core - # pydash # pytest-asyncio # referencing # spglib # swagger-spec-validator # typeguard # typing-inspection -typing-inspection==0.4.1 +typing-inspection==0.4.2 # via # pydantic # pydantic-settings diff --git a/requirements/requirements-ubuntu-latest_py3.12.txt b/requirements/requirements-ubuntu-latest_py3.12.txt index f39aaf801..226fda301 100644 --- a/requirements/requirements-ubuntu-latest_py3.12.txt +++ b/requirements/requirements-ubuntu-latest_py3.12.txt @@ -4,93 +4,48 @@ # # pip-compile --cert=None --client-cert=None --index-url=None --output-file=requirements/requirements-ubuntu-latest_py3.12.txt --pip-args=None pyproject.toml # -aioitertools==0.12.0 - # via maggma annotated-types==0.7.0 # via pydantic -attrs==25.3.0 - # via - # jsonlines - # jsonschema - # referencing -bcrypt==5.0.0 - # via paramiko bibtexparser==1.4.3 # via pymatgen -boto3==1.40.40 - # via maggma -botocore==1.40.40 - # via - # boto3 - # s3transfer certifi==2025.8.3 # via requests -cffi==2.0.0 - # via - # cryptography - # pynacl charset-normalizer==3.4.3 # via requests contourpy==1.3.3 # via matplotlib -cryptography==46.0.1 - # via paramiko cycler==0.12.1 # via matplotlib -dnspython==2.8.0 - # via - # maggma - # pymongo emmet-core==0.84.10 # via mp-api (pyproject.toml) -fonttools==4.60.0 +fonttools==4.60.1 # via matplotlib idna==3.10 # via requests -invoke==2.2.0 - # via paramiko -jmespath==1.0.1 - # via - # boto3 - # botocore joblib==1.5.2 # via pymatgen -jsonlines==4.0.0 - # via maggma -jsonschema==4.25.1 - # via maggma -jsonschema-specifications==2025.9.1 - # via jsonschema kiwisolver==1.4.9 # via matplotlib latexcodec==3.0.1 # via pybtex -maggma==0.72.0 - # via mp-api (pyproject.toml) matplotlib==3.10.6 # via pymatgen -mongomock==4.3.0 - # via maggma monty==2025.3.3 # via # emmet-core - # maggma # mp-api (pyproject.toml) # pymatgen mpmath==1.3.0 # via sympy msgpack==1.1.1 - # via - # maggma - # mp-api (pyproject.toml) -narwhals==2.5.0 + # via mp-api (pyproject.toml) +narwhals==2.6.0 # via plotly networkx==3.5 # via pymatgen numpy==2.3.3 # via # contourpy - # maggma # matplotlib # monty # pandas @@ -99,34 +54,24 @@ numpy==2.3.3 # scipy # spglib orjson==3.11.3 - # via - # maggma - # pymatgen + # via pymatgen packaging==25.0 # via # matplotlib - # mongomock # plotly palettable==3.3.3 # via pymatgen -pandas==2.3.2 - # via - # maggma - # pymatgen -paramiko==4.0.0 - # via sshtunnel +pandas==2.3.3 + # via pymatgen pillow==11.3.0 # via matplotlib plotly==6.3.0 # via pymatgen pybtex==0.25.1 # via emmet-core -pycparser==2.23 - # via cffi pydantic==2.11.9 # via # emmet-core - # maggma # pydantic-settings # pymatgen-io-validation pydantic-core==2.33.2 @@ -134,10 +79,7 @@ pydantic-core==2.33.2 pydantic-settings==2.11.0 # via # emmet-core - # maggma # pymatgen-io-validation -pydash==8.0.5 - # via maggma pymatgen==2025.6.14 # via # emmet-core @@ -145,83 +87,54 @@ pymatgen==2025.6.14 # pymatgen-io-validation pymatgen-io-validation==0.1.2 # via emmet-core -pymongo==4.10.1 - # via maggma -pynacl==1.6.0 - # via paramiko pyparsing==3.2.5 # via # bibtexparser # matplotlib python-dateutil==2.9.0.post0 # via - # botocore - # maggma # matplotlib # pandas python-dotenv==1.1.1 # via pydantic-settings pytz==2025.2 - # via - # mongomock - # pandas + # via pandas pyyaml==6.0.3 # via pybtex -pyzmq==27.1.0 - # via maggma -referencing==0.36.2 - # via - # jsonschema - # jsonschema-specifications requests==2.32.5 # via # mp-api (pyproject.toml) # pymatgen # pymatgen-io-validation -rpds-py==0.27.1 - # via - # jsonschema - # referencing ruamel-yaml==0.18.15 # via - # maggma # monty # pymatgen ruamel-yaml-clib==0.2.14 # via ruamel-yaml -s3transfer==0.14.0 - # via boto3 scipy==1.16.2 # via pymatgen -sentinels==1.1.1 - # via mongomock six==1.17.0 # via python-dateutil smart-open==7.3.1 # via mp-api (pyproject.toml) spglib==2.6.0 # via pymatgen -sshtunnel==0.4.0 - # via maggma sympy==1.14.0 # via pymatgen tabulate==0.9.0 # via pymatgen tqdm==4.67.1 - # via - # maggma - # pymatgen + # via pymatgen typing-extensions==4.15.0 # via # emmet-core # mp-api (pyproject.toml) # pydantic # pydantic-core - # pydash - # referencing # spglib # typing-inspection -typing-inspection==0.4.1 +typing-inspection==0.4.2 # via # pydantic # pydantic-settings @@ -230,11 +143,6 @@ tzdata==2025.2 uncertainties==3.2.3 # via pymatgen urllib3==2.5.0 - # via - # botocore - # requests + # via requests wrapt==1.17.3 # via smart-open - -# The following packages are considered to be unsafe in a requirements file: -# setuptools diff --git a/requirements/requirements-ubuntu-latest_py3.12_extras.txt b/requirements/requirements-ubuntu-latest_py3.12_extras.txt index 8d2f17beb..805c128e6 100644 --- a/requirements/requirements-ubuntu-latest_py3.12_extras.txt +++ b/requirements/requirements-ubuntu-latest_py3.12_extras.txt @@ -4,8 +4,6 @@ # # pip-compile --all-extras --cert=None --client-cert=None --index-url=None --output-file=requirements/requirements-ubuntu-latest_py3.12_extras.txt --pip-args=None pyproject.toml # -aioitertools==0.12.0 - # via maggma alabaster==1.0.0 # via sphinx annotated-types==0.7.0 @@ -18,22 +16,17 @@ asttokens==3.0.0 # via stack-data attrs==25.3.0 # via - # jsonlines # jsonschema # referencing babel==2.17.0 # via sphinx -bcrypt==5.0.0 - # via paramiko bibtexparser==1.4.3 # via pymatgen boltons==25.0.0 # via mpcontribs-client -boto3==1.40.40 - # via - # maggma - # mp-api (pyproject.toml) -botocore==1.40.40 +boto3==1.40.43 + # via mp-api (pyproject.toml) +botocore==1.40.43 # via # boto3 # s3transfer @@ -45,10 +38,6 @@ cachetools==6.2.0 # via mpcontribs-client certifi==2025.8.3 # via requests -cffi==2.0.0 - # via - # cryptography - # pynacl cfgv==3.4.0 # via pre-commit charset-normalizer==3.4.3 @@ -57,8 +46,6 @@ contourpy==1.3.3 # via matplotlib coverage[toml]==7.10.7 # via pytest-cov -cryptography==46.0.1 - # via paramiko custodian==2025.8.13 # via mp-api (pyproject.toml) cycler==0.12.1 @@ -69,7 +56,6 @@ distlib==0.4.0 # via virtualenv dnspython==2.8.0 # via - # maggma # pyisemail # pymongo docutils==0.21.2 @@ -94,7 +80,7 @@ flexcache==0.3 # via pint flexparser==0.4 # via pint -fonttools==4.60.0 +fonttools==4.60.1 # via matplotlib fqdn==1.5.1 # via jsonschema @@ -116,9 +102,7 @@ inflect==7.5.0 # via robocrys iniconfig==2.1.0 # via pytest -invoke==2.2.0 - # via paramiko -ipython==9.5.0 +ipython==9.6.0 # via mpcontribs-client ipython-pygments-lexers==1.1.1 # via ipython @@ -140,8 +124,6 @@ joblib==1.5.2 # scikit-learn json2html==1.3.0 # via mpcontribs-client -jsonlines==4.0.0 - # via maggma jsonpointer==3.0.0 # via jsonschema jsonref==1.1.0 @@ -149,7 +131,6 @@ jsonref==1.1.0 jsonschema[format-nongpl]==4.25.1 # via # bravado-core - # maggma # swagger-spec-validator jsonschema-specifications==2025.9.1 # via jsonschema @@ -161,9 +142,7 @@ latexcodec==3.0.1 # via pybtex lazy-loader==0.4 # via scikit-image -maggma==0.72.0 - # via mp-api (pyproject.toml) -markupsafe==3.0.2 +markupsafe==3.0.3 # via jinja2 matminer==0.9.3 # via robocrys @@ -187,15 +166,12 @@ mdanalysis==2.9.0 # transport-analysis mmtf-python==1.1.3 # via mdanalysis -mongomock==4.3.0 - # via maggma monotonic==1.6 # via bravado monty==2025.3.3 # via # custodian # emmet-core - # maggma # matminer # mp-api (pyproject.toml) # pymatgen @@ -214,7 +190,6 @@ msgpack==1.1.1 # via # bravado # bravado-core - # maggma # mmtf-python # mp-api (pyproject.toml) mypy==1.18.2 @@ -223,7 +198,7 @@ mypy-extensions==1.1.0 # via # mp-api (pyproject.toml) # mypy -narwhals==2.5.0 +narwhals==2.6.0 # via plotly networkx==3.5 # via @@ -238,7 +213,6 @@ numpy==1.26.4 # contourpy # griddataformats # imageio - # maggma # matminer # matplotlib # mdanalysis @@ -265,15 +239,12 @@ numpy==1.26.4 # tidynamics # tifffile orjson==3.11.3 - # via - # maggma - # pymatgen + # via pymatgen packaging==25.0 # via # lazy-loader # matplotlib # mdanalysis - # mongomock # plotly # pytest # scikit-image @@ -281,17 +252,14 @@ packaging==25.0 # statsmodels palettable==3.3.3 # via pymatgen -pandas==2.3.2 +pandas==2.3.3 # via - # maggma # matminer # mpcontribs-client # pymatgen # seaborn # solvation-analysis # statsmodels -paramiko==4.0.0 - # via sshtunnel parso==0.8.5 # via jedi pathspec==0.12.1 @@ -343,12 +311,9 @@ pycodestyle==2.14.0 # via # flake8 # mp-api (pyproject.toml) -pycparser==2.23 - # via cffi pydantic==2.11.9 # via # emmet-core - # maggma # pydantic-settings # pymatgen-io-validation pydantic-core==2.33.2 @@ -356,10 +321,7 @@ pydantic-core==2.33.2 pydantic-settings==2.11.0 # via # emmet-core - # maggma # pymatgen-io-validation -pydash==8.0.5 - # via maggma pyflakes==3.4.0 # via flake8 pygments==2.19.2 @@ -390,13 +352,10 @@ pymatgen-analysis-diffusion==2024.7.15 # via emmet-core pymatgen-io-validation==0.1.2 # via emmet-core -pymongo==4.10.1 +pymongo==4.15.2 # via - # maggma # matminer # mpcontribs-client -pynacl==1.6.0 - # via paramiko pyparsing==3.2.5 # via # bibtexparser @@ -423,7 +382,6 @@ python-dateutil==2.9.0.post0 # botocore # bravado # bravado-core - # maggma # matplotlib # pandas python-dotenv==1.1.1 @@ -431,7 +389,6 @@ python-dotenv==1.1.1 pytz==2025.2 # via # bravado-core - # mongomock # pandas pyyaml==6.0.3 # via @@ -440,8 +397,6 @@ pyyaml==6.0.3 # pre-commit # pybtex # swagger-spec-validator -pyzmq==27.1.0 - # via maggma rdkit==2025.3.6 # via solvation-analysis referencing==0.36.2 @@ -477,7 +432,6 @@ rpds-py==0.27.1 ruamel-yaml==0.18.15 # via # custodian - # maggma # monty # pymatgen # robocrys @@ -506,8 +460,6 @@ seekpath==2.1.0 # via emmet-core semantic-version==2.10.0 # via mpcontribs-client -sentinels==1.1.1 - # via mongomock shapely==2.1.2 # via pymatgen-analysis-alloys simplejson==3.20.2 @@ -546,8 +498,6 @@ sphinxcontrib-qthelp==2.0.0 # via sphinx sphinxcontrib-serializinghtml==2.0.0 # via sphinx -sshtunnel==0.4.0 - # via maggma stack-data==0.6.3 # via ipython statsmodels==0.14.5 @@ -568,11 +518,10 @@ threadpoolctl==3.6.0 # scikit-learn tidynamics==1.1.2 # via transport-analysis -tifffile==2025.9.20 +tifffile==2025.9.30 # via scikit-image tqdm==4.67.1 # via - # maggma # matminer # mdanalysis # mpcontribs-client @@ -602,14 +551,13 @@ typing-extensions==4.15.0 # pint # pydantic # pydantic-core - # pydash # pytest-asyncio # referencing # spglib # swagger-spec-validator # typeguard # typing-inspection -typing-inspection==0.4.1 +typing-inspection==0.4.2 # via # pydantic # pydantic-settings diff --git a/tests/materials/core_function.py b/tests/materials/core_function.py index bb7138c62..5d5226236 100644 --- a/tests/materials/core_function.py +++ b/tests/materials/core_function.py @@ -66,7 +66,6 @@ def client_search_testing( f"Parameter '{param}' with type '{param_type}' was not " "properly identified in the generic search method test." ) - doc = search_method(**q)[0].model_dump() for sub_field in sub_doc_fields: diff --git a/tests/materials/test_electrodes.py b/tests/materials/test_electrodes.py index e4541af79..ff651cdca 100644 --- a/tests/materials/test_electrodes.py +++ b/tests/materials/test_electrodes.py @@ -4,16 +4,26 @@ import pytest from pymatgen.core.periodic_table import Element -from mp_api.client.routes.materials.electrodes import ElectrodeRester +from mp_api.client.routes.materials.electrodes import ( + ElectrodeRester, + ConversionElectrodeRester, +) @pytest.fixture -def rester(): +def insertion_rester(): rester = ElectrodeRester() yield rester rester.session.close() +@pytest.fixture +def conversion_rester(): + rester = ConversionElectrodeRester() + yield rester + rester.session.close() + + excluded_params = [ "sort_fields", "chunk_size", @@ -43,8 +53,8 @@ def rester(): @pytest.mark.skipif(os.getenv("MP_API_KEY", None) is None, reason="No API key found.") -def test_client(rester): - search_method = rester.search +def test_insertion_client(insertion_rester): + search_method = insertion_rester.search client_search_testing( search_method=search_method, @@ -53,3 +63,21 @@ def test_client(rester): custom_field_tests=custom_field_tests, sub_doc_fields=sub_doc_fields, ) + + +@pytest.mark.skipif(os.getenv("MP_API_KEY", None) is None, reason="No API key found.") +def test_conversion_client(conversion_rester): + search_method = conversion_rester.search + + excl = ConversionElectrodeRester._exclude_search_fields + client_search_testing( + search_method=search_method, + excluded_params=excluded_params + excl, + alt_name_dict=alt_name_dict, + custom_field_tests={ + "battery_ids": ["mp-1067_Al"], + "working_ion": Element("Li"), + "exclude_elements": ["Co", "O"], + }, + sub_doc_fields=sub_doc_fields, + ) diff --git a/tests/materials/test_tasks.py b/tests/materials/test_tasks.py index 2cb27031c..b35dfd938 100644 --- a/tests/materials/test_tasks.py +++ b/tests/materials/test_tasks.py @@ -1,8 +1,8 @@ import os from core_function import client_search_testing -from datetime import datetime import pytest +from emmet.core.utils import utcnow from mp_api.client.routes.materials.tasks import TaskRester @@ -34,7 +34,7 @@ def rester(): custom_field_tests = { "chemsys": "Si-O", - "last_updated": (None, datetime.utcnow()), + "last_updated": (None, utcnow()), "task_ids": ["mp-149"], } # type: dict diff --git a/tests/test_client.py b/tests/test_client.py index 25bf18124..49a775048 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -25,6 +25,7 @@ "materials_substrates", "materials_synthesis", "materials_similarity", + "materials_tasks", ] special_resters = ["materials_charge_density", "doi"] @@ -36,7 +37,6 @@ # "tasks", # "bonds", "materials_xas", - "materials_tasks", "materials_elasticity", "materials_fermi", "materials_alloys",