optimade-maker 0.7.2__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/.github/workflows/ci.yml +6 -6
  2. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/.gitignore +1 -0
  3. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/PKG-INFO +14 -15
  4. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/README.md +4 -2
  5. optimade_maker-0.9.0/examples/properties/.testing/first_entry.json +1 -0
  6. optimade_maker-0.9.0/examples/properties/optimade.yaml +45 -0
  7. optimade_maker-0.9.0/examples/properties/prop1.csv +3 -0
  8. optimade_maker-0.9.0/examples/properties/prop2.json +18 -0
  9. optimade_maker-0.9.0/examples/simple_zip_of_cif/.gitignore +2 -0
  10. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/simple_zip_of_cif/.testing/first_entry.json +1 -1
  11. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/simple_zip_of_cif/optimade.yaml +3 -8
  12. optimade_maker-0.9.0/examples/simple_zip_of_cif/properties.csv +3 -0
  13. optimade_maker-0.9.0/examples/simple_zip_of_cif/structures.zip +0 -0
  14. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/pyproject.toml +9 -11
  15. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/convert.py +4 -4
  16. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/parsers.py +109 -58
  17. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/serve.py +5 -4
  18. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker.egg-info/PKG-INFO +14 -15
  19. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker.egg-info/SOURCES.txt +7 -11
  20. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker.egg-info/requires.txt +9 -15
  21. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/tests/test_convert.py +5 -0
  22. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/tests/test_serve.py +14 -4
  23. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/uv.lock +20 -28
  24. optimade_maker-0.7.2/examples/simple_zip_of_cif/data.csv +0 -3
  25. optimade_maker-0.7.2/scripts/aiida_to_jsonl/aiida_to_jsonl.py +0 -69
  26. optimade_maker-0.7.2/scripts/cifs_to_jsonl/cifs/55c564f6-ac6a-4122-b8d9-0ad9fe61e961.cif +0 -46
  27. optimade_maker-0.7.2/scripts/cifs_to_jsonl/cifs/991bec7a-b3a8-49af-ba6d-be5afd685cd4.cif +0 -34
  28. optimade_maker-0.7.2/scripts/cifs_to_jsonl/cifs/cc1a41b1-a841-4818-baf1-a6c1441dc52a.cif +0 -52
  29. optimade_maker-0.7.2/scripts/cifs_to_jsonl/cifs_to_jsonl.py +0 -50
  30. optimade_maker-0.7.2/scripts/cifs_to_jsonl/optimade.jsonl +0 -4
  31. optimade_maker-0.7.2/scripts/jsonl_server/load_data.py +0 -68
  32. optimade_maker-0.7.2/scripts/jsonl_server/requirements.txt +0 -1
  33. optimade_maker-0.7.2/scripts/odbx_file_serialization/requirements.txt +0 -1
  34. optimade_maker-0.7.2/scripts/odbx_file_serialization/run.py +0 -33
  35. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/.github/CODEOWNERS +0 -0
  36. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/.github/dependabot.yml +0 -0
  37. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/.github/workflows/release_and_publish.yml +0 -0
  38. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/.pre-commit-config.yaml +0 -0
  39. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/LICENSE +0 -0
  40. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/aiida_archive/.gitignore +0 -0
  41. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/aiida_archive/.testing/first_entry.json +0 -0
  42. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/aiida_archive/README.md +0 -0
  43. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/aiida_archive/data.csv +0 -0
  44. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/aiida_archive/example.aiida +0 -0
  45. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/aiida_archive/optimade.yaml +0 -0
  46. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/bzipped_pymatgen/.gitignore +0 -0
  47. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/bzipped_pymatgen/.testing/first_entry.json +0 -0
  48. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/bzipped_pymatgen/optimade.yaml +0 -0
  49. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/bzipped_pymatgen/part_1.json.bz2 +0 -0
  50. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/direct_from_jsonl/.testing/first_entry.json +0 -0
  51. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/direct_from_jsonl/optimade.jsonl +0 -0
  52. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/direct_from_jsonl/optimade.yaml +0 -0
  53. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/direct_from_jsonl_gz/.gitignore +0 -0
  54. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/direct_from_jsonl_gz/.testing/first_entry.json +0 -0
  55. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/direct_from_jsonl_gz/example.jsonl.gz +0 -0
  56. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/direct_from_jsonl_gz/optimade.yaml +0 -0
  57. {optimade_maker-0.7.2/examples/simple_zip_of_cif → optimade_maker-0.9.0/examples/properties}/.gitignore +0 -0
  58. {optimade_maker-0.7.2/examples/simple_zip_of_cif → optimade_maker-0.9.0/examples/properties}/structures.zip +0 -0
  59. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/xyz_files_no_compression/.gitignore +0 -0
  60. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/xyz_files_no_compression/.testing/first_entry.json +0 -0
  61. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/xyz_files_no_compression/C_1.xyz +0 -0
  62. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/xyz_files_no_compression/H_1.xyz +0 -0
  63. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/xyz_files_no_compression/H_2.xyz +0 -0
  64. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/xyz_files_no_compression/data.csv +0 -0
  65. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/xyz_files_no_compression/optimade.yaml +0 -0
  66. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif/.gitignore +0 -0
  67. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif/.testing/first_entry.json +0 -0
  68. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif/README.md +0 -0
  69. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif/data.tar.gz +0 -0
  70. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif/optimade.yaml +0 -0
  71. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif/override_config.json +0 -0
  72. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif/structures.zip +0 -0
  73. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif_and_xyz/.gitignore +0 -0
  74. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif_and_xyz/.testing/first_entry.json +0 -0
  75. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif_and_xyz/optimade.yaml +0 -0
  76. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/examples/zip_of_cif_and_xyz/structures.zip +0 -0
  77. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/setup.cfg +0 -0
  78. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/__init__.py +0 -0
  79. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/aiida_plugin/__init__.py +0 -0
  80. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/aiida_plugin/config.py +0 -0
  81. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/cli.py +0 -0
  82. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/config.py +0 -0
  83. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/logger.py +0 -0
  84. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker/mongo_utils.py +0 -0
  85. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker.egg-info/dependency_links.txt +0 -0
  86. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker.egg-info/entry_points.txt +0 -0
  87. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/src/optimade_maker.egg-info/top_level.txt +0 -0
  88. {optimade_maker-0.7.2 → optimade_maker-0.9.0}/tests/test_yaml.py +0 -0
@@ -63,21 +63,21 @@ jobs:
63
63
  version: "0.8.x"
64
64
  enable-cache: true
65
65
 
66
- - name: Install latest compatible versions of immediate dependencies
66
+ - name: Install the minimum environment with testing
67
67
  run: |
68
- uv sync --locked --all-extras --dev
68
+ uv sync --locked --extra dev
69
69
 
70
70
  - name: Run tests
71
71
  run: |
72
72
  uv run pytest -vv --cov=./src/optimade_maker --cov-report=xml --cov-report=term ./tests
73
73
 
74
- - name: Reinstall environment without aiida
74
+ - name: Reinstall the full environment with all extras
75
75
  run: |
76
- uv sync --locked --extra core --extra tests
76
+ uv sync --locked --all-extras --dev
77
77
 
78
- - name: Rerun tests without aiida
78
+ - name: Rerun tests
79
79
  run: |
80
- uv run pytest -vv --cov=./src/optimade_maker --cov-report=xml --cov-report=term ./tests --ignore=./tests/aiida
80
+ uv run pytest -vv --cov=./src/optimade_maker --cov-report=xml --cov-report=term ./tests
81
81
 
82
82
  - name: Upload coverage
83
83
  uses: codecov/codecov-action@v3
@@ -129,3 +129,4 @@ dmypy.json
129
129
  .pyre/
130
130
 
131
131
  .vscode
132
+ .ruff_cache
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optimade-maker
3
- Version: 0.7.2
3
+ Version: 0.9.0
4
4
  Summary: Tools for making OPTIMADE APIs from raw structural data.
5
5
  License: MIT
6
6
  Keywords: optimade,jsonapi,materials
@@ -15,30 +15,27 @@ Classifier: Topic :: Scientific/Engineering
15
15
  Requires-Python: <3.13,>=3.10
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
- Requires-Dist: pydantic~=2.2
19
- Requires-Dist: optimade[server]~=1.3.1
20
- Requires-Dist: pymongo
18
+ Requires-Dist: pydantic<3,>=2
19
+ Requires-Dist: optimade[server]~=1.4
20
+ Requires-Dist: pymongo<5,>=4
21
21
  Requires-Dist: pyyaml~=6.0
22
22
  Requires-Dist: tqdm~=4.65
23
23
  Requires-Dist: requests~=2.31
24
- Requires-Dist: numpy<3,>=1.22
25
24
  Requires-Dist: click~=8.1
26
- Provides-Extra: ase
27
- Requires-Dist: ase~=3.22; extra == "ase"
25
+ Requires-Dist: pandas<3,>=1.5
26
+ Requires-Dist: ase~=3.22
28
27
  Provides-Extra: pymatgen
29
28
  Requires-Dist: pymatgen>=2023.9; extra == "pymatgen"
30
- Provides-Extra: pandas
31
- Requires-Dist: pandas<3,>=1.5; extra == "pandas"
32
29
  Provides-Extra: aiida
33
- Requires-Dist: aiida-core>=2.6.3; extra == "aiida"
34
- Provides-Extra: core
35
- Requires-Dist: optimade-maker[ase,pandas,pymatgen]; extra == "core"
30
+ Requires-Dist: aiida-core~=2.6; extra == "aiida"
36
31
  Provides-Extra: ingest
37
- Requires-Dist: optimade-maker[aiida,ase,pandas,pymatgen]; extra == "ingest"
32
+ Requires-Dist: optimade-maker[aiida,pymatgen]; extra == "ingest"
38
33
  Provides-Extra: tests
39
34
  Requires-Dist: pytest~=8.3; extra == "tests"
40
35
  Requires-Dist: pytest-cov~=6.0; extra == "tests"
36
+ Requires-Dist: numpy<3,>=1.22; extra == "tests"
41
37
  Provides-Extra: dev
38
+ Requires-Dist: optimade-maker[tests]; extra == "dev"
42
39
  Requires-Dist: ruff; extra == "dev"
43
40
  Requires-Dist: pre-commit; extra == "dev"
44
41
  Requires-Dist: mypy; extra == "dev"
@@ -75,6 +72,8 @@ This repository contains the src/optimade-maker Python package and the correspon
75
72
  Install with
76
73
 
77
74
  ```bash
75
+ pip install optimade-maker
76
+ # or to get all ingestion plugins (e.g. pymatgen, aiida):
78
77
  pip install optimade-maker[ingest]
79
78
  ```
80
79
 
@@ -106,8 +105,8 @@ entries:
106
105
  - file: properties.csv
107
106
  property_definitions:
108
107
  - name: energy
109
- title: Total energy per atom
110
- description: DFT total energy per atom
108
+ title: Total energy
109
+ description: DFT total energy
111
110
  unit: eV/atom
112
111
  type: float
113
112
  ```
@@ -29,6 +29,8 @@ This repository contains the src/optimade-maker Python package and the correspon
29
29
  Install with
30
30
 
31
31
  ```bash
32
+ pip install optimade-maker
33
+ # or to get all ingestion plugins (e.g. pymatgen, aiida):
32
34
  pip install optimade-maker[ingest]
33
35
  ```
34
36
 
@@ -60,8 +62,8 @@ entries:
60
62
  - file: properties.csv
61
63
  property_definitions:
62
64
  - name: energy
63
- title: Total energy per atom
64
- description: DFT total energy per atom
65
+ title: Total energy
66
+ description: DFT total energy
65
67
  unit: eV/atom
66
68
  type: float
67
69
  ```
@@ -0,0 +1 @@
1
+ {"id": "set1/101", "type": "structures", "links": null, "meta": null, "attributes": {"immutable_id": "structures.zip/cifs/set1/101.cif", "last_modified": "2026-02-16T13:34:52.085191", "elements": ["Ba", "C", "N", "S"], "nelements": 4, "elements_ratios": [0.14285714285714285, 0.2857142857142857, 0.2857142857142857, 0.2857142857142857], "chemical_formula_descriptive": "C4Ba2N4S4", "chemical_formula_reduced": "BaC2N2S2", "chemical_formula_hill": null, "chemical_formula_anonymous": "A2B2C2D", "dimension_types": [1, 1, 1], "nperiodic_dimensions": 3, "lattice_vectors": [[6.3587627540404945, 0.0, 0.0], [-2.672647488887009, 5.769819681958754, 0.0], [0.25844951934994664, -0.16511343006546234, 8.71190314896161]], "space_group_symmetry_operations_xyz": null, "space_group_symbol_hall": null, "space_group_symbol_hermann_mauguin": null, "space_group_symbol_hermann_mauguin_extended": null, "space_group_it_number": null, "cartesian_site_positions": [[3.4987802863851005, 5.049341739457014, 6.533927361693402], [0.4457844982025419, 0.5553645123824795, 2.177975787264444], [0.37416734784252487, 2.642448780492868, 5.291889331690525], [2.6281157156591126, 1.202488098280357, 7.775965391700042], [3.5703974366609055, 2.962257471400425, 3.420013817271085], [1.316449068841148, 4.402218153614962, 0.9359377571616379], [0.3512600296798777, 4.156339511491648, 5.900755701251229], [4.011419364830723, 1.818004876809729, 7.167099022143105], [3.5933047547393455, 1.4483667404554434, 2.8111474477141454], [-0.06685458032729186, 3.786701375083563, 1.5448041268185058], [0.3773832460379156, 1.5350786992068879, 4.8597303327393915], [1.621957615426957, 0.7399681351855376, 8.208124390751108], [3.567181538468686, 4.069627552684378, 3.8521728163221476], [2.3226071690796486, 4.864738116705728, 0.5037787583104332]], "nsites": 14, "species": [{"name": "N", "chemical_symbols": ["N"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}, {"name": "Ba", "chemical_symbols": ["Ba"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}, {"name": "C", "chemical_symbols": ["C"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}, {"name": "S", "chemical_symbols": ["S"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}], "species_at_sites": ["Ba", "Ba", "C", "C", "C", "C", "S", "S", "S", "S", "N", "N", "N", "N"], "assemblies": null, "structure_features": [], "_optimake_json_list": [[1, 2.0, 3]], "_optimake_dict_property": {"a": 1, "b": 2}, "_optimake_list_property": [[1, 2.0, 3], [3, 2, 3], [1, 5, 6]], "_optimake_json_nested_dict": {"a": 1, "b": {"c": 2, "d": [1, 2, 3]}}, "_optimake_json_float": 2.5, "_optimake_energy": 2.5}, "relationships": null}
@@ -0,0 +1,45 @@
1
+ config_version: 0.1.1
2
+ database_description: Simple database
3
+
4
+ entries:
5
+ - entry_type: structures
6
+ entry_paths:
7
+ - file: structures.zip
8
+ matches:
9
+ - cifs/*/*.cif
10
+ # (optional) property file and definitions:
11
+ property_paths:
12
+ - file: prop1.csv
13
+ - file: prop2.json
14
+ property_definitions:
15
+ # properties in the csv file
16
+ - name: energy
17
+ title: Total energy per atom
18
+ description: The total energy per atom as computed by DFT
19
+ unit: eV/atom
20
+ type: float
21
+ - name: list_property
22
+ title: List property
23
+ description: A 3x3 matrix property
24
+ type: list
25
+ - name: dict_property
26
+ title: Dictionary property
27
+ description: A dictionary property
28
+ type: dictionary
29
+ # properties in the json file
30
+ - name: json_float
31
+ title: JSON float property
32
+ description: A JSON float property
33
+ type: float
34
+ aliases:
35
+ - json_float_alias1
36
+ - json_float_alias2
37
+ - json_float_alias3
38
+ - name: json_list
39
+ title: JSON list property
40
+ description: A JSON list property
41
+ type: list
42
+ - name: json_nested_dict
43
+ title: JSON nested dictionary property
44
+ description: A nested dictionary property in JSON format
45
+ type: dictionary
@@ -0,0 +1,3 @@
1
+ id,energy,list_property,dict_property
2
+ set1/101,2.5,"[[1, 2.0, 3], [3, 2, 3], [1, 5, 6]]","{""a"": 1, ""b"": 2}"
3
+ structures.zip/cifs/set2/102.cif,3.2,,
@@ -0,0 +1,18 @@
1
+ {
2
+ "set1/101": {
3
+ "json_float_alias2": 2.5,
4
+ "json_list": [[1, 2.0, 3]],
5
+ "json_nested_dict": {
6
+ "a": 1,
7
+ "b": {
8
+ "c": 2,
9
+ "d": [1, 2, 3]
10
+ }
11
+ }
12
+ },
13
+ "structures.zip/cifs/set2/102.cif": {
14
+ "json_float_alias3": 3.2,
15
+ "json_list": null,
16
+ "json_nested_dict": null
17
+ }
18
+ }
@@ -0,0 +1,2 @@
1
+ cifs
2
+ optimade.jsonl
@@ -1 +1 @@
1
- {"id": "set1/101", "type": "structures", "links": null, "meta": null, "attributes": {"immutable_id": "structures.zip/cifs/set1/101.cif", "last_modified": "2025-07-22T13:03:07.681100", "elements": ["Ba", "C", "N", "S"], "nelements": 4, "elements_ratios": [0.14285714285714285, 0.2857142857142857, 0.2857142857142857, 0.2857142857142857], "chemical_formula_descriptive": "C4Ba2N4S4", "chemical_formula_reduced": "BaC2N2S2", "chemical_formula_hill": null, "chemical_formula_anonymous": "A2B2C2D", "dimension_types": [1, 1, 1], "nperiodic_dimensions": 3, "lattice_vectors": [[6.3587627540404945, 0.0, 0.0], [-2.672647488887009, 5.769819681958754, 0.0], [0.25844951934994664, -0.16511343006546234, 8.71190314896161]], "space_group_symmetry_operations_xyz": null, "space_group_symbol_hall": null, "space_group_symbol_hermann_mauguin": null, "space_group_symbol_hermann_mauguin_extended": null, "space_group_it_number": null, "cartesian_site_positions": [[3.4987802863851005, 5.049341739457014, 6.533927361693402], [0.4457844982025419, 0.5553645123824795, 2.177975787264444], [0.37416734784252487, 2.642448780492868, 5.291889331690525], [2.6281157156591126, 1.202488098280357, 7.775965391700042], [3.5703974366609055, 2.962257471400425, 3.420013817271085], [1.316449068841148, 4.402218153614962, 0.9359377571616379], [0.3512600296798777, 4.156339511491648, 5.900755701251229], [4.011419364830723, 1.818004876809729, 7.167099022143105], [3.5933047547393455, 1.4483667404554434, 2.8111474477141454], [-0.06685458032729186, 3.786701375083563, 1.5448041268185058], [0.3773832460379155, 1.5350786992068879, 4.8597303327393915], [1.621957615426957, 0.7399681351855376, 8.208124390751108], [3.567181538468686, 4.069627552684378, 3.8521728163221476], [2.3226071690796477, 4.864738116705728, 0.5037787583104332]], "nsites": 14, "species": [{"name": "Ba", "chemical_symbols": ["Ba"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}, {"name": "C", "chemical_symbols": ["C"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}, {"name": "N", "chemical_symbols": ["N"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}, {"name": "S", "chemical_symbols": ["S"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}], "species_at_sites": ["Ba", "Ba", "C", "C", "C", "C", "S", "S", "S", "S", "N", "N", "N", "N"], "assemblies": null, "structure_features": [], "_optimake_energy": 2.5, "_optimake_list_property": [[1, 2.0, 3], [3, 2, 3], [1, 5, 6]]}, "relationships": null}
1
+ {"id": "set1/101", "type": "structures", "links": null, "meta": null, "attributes": {"immutable_id": "structures.zip/cifs/set1/101.cif", "last_modified": "2026-02-16T13:37:21.465430", "elements": ["Ba", "C", "N", "S"], "nelements": 4, "elements_ratios": [0.14285714285714285, 0.2857142857142857, 0.2857142857142857, 0.2857142857142857], "chemical_formula_descriptive": "C4Ba2N4S4", "chemical_formula_reduced": "BaC2N2S2", "chemical_formula_hill": null, "chemical_formula_anonymous": "A2B2C2D", "dimension_types": [1, 1, 1], "nperiodic_dimensions": 3, "lattice_vectors": [[6.3587627540404945, 0.0, 0.0], [-2.672647488887009, 5.769819681958754, 0.0], [0.25844951934994664, -0.16511343006546234, 8.71190314896161]], "space_group_symmetry_operations_xyz": null, "space_group_symbol_hall": null, "space_group_symbol_hermann_mauguin": null, "space_group_symbol_hermann_mauguin_extended": null, "space_group_it_number": null, "cartesian_site_positions": [[3.4987802863851005, 5.049341739457014, 6.533927361693402], [0.4457844982025419, 0.5553645123824795, 2.177975787264444], [0.37416734784252487, 2.642448780492868, 5.291889331690525], [2.6281157156591126, 1.202488098280357, 7.775965391700042], [3.5703974366609055, 2.962257471400425, 3.420013817271085], [1.316449068841148, 4.402218153614962, 0.9359377571616379], [0.3512600296798777, 4.156339511491648, 5.900755701251229], [4.011419364830723, 1.818004876809729, 7.167099022143105], [3.5933047547393455, 1.4483667404554434, 2.8111474477141454], [-0.06685458032729186, 3.786701375083563, 1.5448041268185058], [0.3773832460379156, 1.5350786992068879, 4.8597303327393915], [1.621957615426957, 0.7399681351855376, 8.208124390751108], [3.567181538468686, 4.069627552684378, 3.8521728163221476], [2.3226071690796486, 4.864738116705728, 0.5037787583104332]], "nsites": 14, "species": [{"name": "C", "chemical_symbols": ["C"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}, {"name": "S", "chemical_symbols": ["S"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}, {"name": "Ba", "chemical_symbols": ["Ba"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}, {"name": "N", "chemical_symbols": ["N"], "concentration": [1.0], "mass": null, "original_name": null, "attached": null, "nattached": null}], "species_at_sites": ["Ba", "Ba", "C", "C", "C", "C", "S", "S", "S", "S", "N", "N", "N", "N"], "assemblies": null, "structure_features": [], "_optimake_energy": 2.5}, "relationships": null}
@@ -9,15 +9,10 @@ entries:
9
9
  - cifs/*/*.cif
10
10
  # (optional) property file and definitions:
11
11
  property_paths:
12
- - file: data.csv
12
+ - file: properties.csv
13
13
  property_definitions:
14
14
  - name: energy
15
- title: Total energy per atom
16
- description: The total energy per atom as computed by DFT
15
+ title: Total energy
16
+ description: DFT total energy
17
17
  unit: eV/atom
18
18
  type: float
19
- - name: list_property
20
- title: List property
21
- description: A 3x3 matrix property encoded as a string
22
- type: list
23
- unit: null
@@ -0,0 +1,3 @@
1
+ id,energy
2
+ set1/101,2.5
3
+ structures.zip/cifs/set2/102.cif,3.2
@@ -19,14 +19,15 @@ classifiers = [
19
19
  ]
20
20
 
21
21
  dependencies = [
22
- "pydantic~=2.2",
23
- "optimade[server]~=1.3.1",
24
- "pymongo",
22
+ "pydantic>=2,<3",
23
+ "optimade[server]~=1.4",
24
+ "pymongo>=4,<5",
25
25
  "pyyaml~=6.0",
26
26
  "tqdm~=4.65",
27
27
  "requests~=2.31",
28
- "numpy >= 1.22, < 3",
29
28
  "click~=8.1",
29
+ "pandas>=1.5,<3",
30
+ "ase~=3.22",
30
31
  ]
31
32
 
32
33
  [project.scripts]
@@ -41,14 +42,11 @@ fallback_version = "0.1.0"
41
42
  version_scheme = "post-release"
42
43
 
43
44
  [project.optional-dependencies]
44
- ase = ["ase ~= 3.22"]
45
45
  pymatgen = ["pymatgen >= 2023.9"]
46
- pandas = ["pandas >= 1.5, < 3"]
47
- aiida = ["aiida-core >= 2.6.3"]
48
- core = ["optimade-maker[ase,pymatgen,pandas]"]
49
- ingest = ["optimade-maker[ase,pymatgen,pandas,aiida]"]
50
- tests = ["pytest~=8.3", "pytest-cov~=6.0"]
51
- dev = ["ruff", "pre-commit", "mypy"]
46
+ aiida = ["aiida-core ~= 2.6"]
47
+ ingest = ["optimade-maker[pymatgen,aiida]"]
48
+ tests = ["pytest~=8.3", "pytest-cov~=6.0", "numpy >= 1.22, < 3"]
49
+ dev = ["optimade-maker[tests]", "ruff", "pre-commit", "mypy"]
52
50
 
53
51
  [tool.ruff]
54
52
  target-version = "py311"
@@ -173,7 +173,7 @@ def inflate_archive(archive_path: Path, data_path: Path) -> None:
173
173
  # If .tar in filename suffixes, use `tarfile`'s compression detection
174
174
  elif ".tar" in real_path.suffixes:
175
175
  with tarfile.open(real_path, "r") as tar:
176
- tar.extractall(path=real_path.parent)
176
+ tar.extractall(path=real_path.parent, filter="data")
177
177
 
178
178
  # Otherwise assume this is a single compressed file
179
179
  # Decompress it and write it using the appropriate
@@ -306,11 +306,11 @@ def _parse_entries(
306
306
  entry_ids.append(id_root)
307
307
  break
308
308
  except Exception as exc:
309
- exceptions[parser] = exc
309
+ exceptions[parser.__name__] = exc
310
310
  continue
311
311
  else:
312
312
  raise RuntimeError(
313
- f"None of the provided parsers {ENTRY_PARSERS[entry_type]} could parse {_path}. Errors: {exceptions}"
313
+ f"None of the provided parsers could parse {_path}. Errors: {exceptions}"
314
314
  )
315
315
 
316
316
  if len(set(entry_ids)) != len(entry_ids):
@@ -573,7 +573,7 @@ def construct_entries_from_files(
573
573
  try:
574
574
  entry = converter(
575
575
  entry,
576
- properties=entry_config.property_definitions,
576
+ prop_defs=entry_config.property_definitions,
577
577
  prefix=provider_prefix,
578
578
  ) # type: ignore[call-arg]
579
579
  if not isinstance(entry, dict):
@@ -3,19 +3,8 @@ import warnings
3
3
  from pathlib import Path
4
4
  from typing import Any, Callable
5
5
 
6
- import numpy as np
7
-
8
- try:
9
- import ase.io
10
- import pandas
11
- import pymatgen.core
12
- import pymatgen.entries.computed_entries
13
- from pymatgen.entries.computed_entries import ComputedStructureEntry
14
- except ImportError as exc:
15
- raise ImportError(
16
- "The parsers module requires the `ingest` extra of this package to be installed."
17
- ) from exc
18
-
6
+ import ase.io
7
+ import pandas
19
8
  from optimade.adapters import Structure
20
9
  from optimade.models import DataType, EntryResource
21
10
 
@@ -24,7 +13,7 @@ from optimade_maker.config import PropertyDefinition
24
13
 
25
14
  def load_csv_file(
26
15
  p: Path,
27
- properties: list[PropertyDefinition] | None = None,
16
+ prop_defs: list[PropertyDefinition] | None = None,
28
17
  ) -> dict[str, dict[str, Any]]:
29
18
  """Parses a CSV file found at path `p` and returns a dictionary
30
19
  of properties keyed by ID.
@@ -34,7 +23,7 @@ def load_csv_file(
34
23
 
35
24
  Parameters:
36
25
  p: Path to the CSV file.
37
- properties: List of property definitions to extract from the CSV file.
26
+ prop_defs: List of property definitions to extract from the CSV file.
38
27
 
39
28
  Returns:
40
29
  A dictionary of ID -> properties.
@@ -55,40 +44,79 @@ def load_csv_file(
55
44
  df["id"] = df[id_key]
56
45
  df = df.set_index("id")
57
46
 
58
- for prop in properties or []:
47
+ for prop_def in prop_defs or []:
59
48
  # loop through any property aliases, saving the value if found and only checking
60
49
  # the real name if not
61
- for alias in prop.aliases or []:
50
+ for alias in prop_def.aliases or []:
62
51
  if alias in df:
63
- df[prop.name] = df[alias]
52
+ df[prop_def.name] = df[alias]
64
53
  df.drop(columns=[alias], inplace=True)
65
54
  break
66
55
 
67
- # Check for list/array-valued properties, being careful
68
- # to reinterpret NaNs back into JSON `null` before trying
69
- for prop in properties or []:
70
- if prop.type == DataType.LIST:
56
+ # For list and dict types, interpret string values as JSON
57
+ for prop_def in prop_defs or []:
58
+ if (
59
+ prop_def.type in (DataType.LIST, DataType.DICTIONARY)
60
+ and prop_def.name in df.columns
61
+ ):
62
+ # Replace NaN -> "null"
63
+ df[prop_def.name] = df[prop_def.name].fillna("null")
71
64
  try:
72
- df[prop.name] = (
73
- df[prop.name]
74
- .fillna(np.nan)
75
- .replace([np.nan], ["null"])
76
- .apply(json.loads)
65
+ df[prop_def.name] = df[prop_def.name].apply(
66
+ lambda v: json.loads(v) if isinstance(v, str) else v
77
67
  )
78
68
  except Exception as exc:
79
69
  warnings.warn(
80
- f"Tried to interpret property {prop.name!r} as a list, but an error was raised: {exc.__class__.__name__}: {exc!r}"
70
+ f"Tried to interpret property {prop_def.name!r} as JSON, but got "
71
+ f"{exc.__class__.__name__}: {exc!r}"
81
72
  )
82
- pass
83
73
 
84
74
  return df.to_dict(orient="index")
85
75
 
86
76
 
77
+ def load_json_file(
78
+ p: Path,
79
+ prop_defs: list[PropertyDefinition] | None = None,
80
+ ) -> dict[str, dict[str, Any]]:
81
+ """Parses a JSON file found at path `p` and returns a dictionary
82
+ of properties keyed by ID.
83
+
84
+ Expects either a list of objects (each with an "id" field) or a dictionary
85
+ already keyed by IDs. If a list is provided, the "id" field of each object
86
+ will be used as the dictionary key.
87
+
88
+ Parameters:
89
+ p: Path to the JSON file.
90
+ prop_defs: List of property definitions to extract from the JSON data.
91
+
92
+ Returns:
93
+ A dictionary of ID -> properties.
94
+ """
95
+
96
+ with open(p, "r") as f:
97
+ data = json.load(f)
98
+
99
+ if isinstance(data, list):
100
+ data_dict = {}
101
+ for item in data:
102
+ id_value = item.get("id")
103
+ if not id_value:
104
+ raise RuntimeError(f"JSON item {item} missing 'id'")
105
+ data_dict[id_value] = item
106
+ data = data_dict
107
+
108
+ for prop_def in prop_defs or []:
109
+ for alias in prop_def.aliases or []:
110
+ for row_id, row in data.items():
111
+ if alias in row:
112
+ row[prop_def.name] = row.pop(alias)
113
+
114
+ return data
115
+
116
+
87
117
  PROPERTY_PARSERS: dict[
88
118
  str, list[Callable[[Path, list[PropertyDefinition] | None], Any]]
89
- ] = {
90
- ".csv": [load_csv_file],
91
- }
119
+ ] = {".csv": [load_csv_file], ".json": [load_json_file]}
92
120
 
93
121
  TYPE_MAP: dict[DataType, type] = {
94
122
  DataType.FLOAT: float,
@@ -98,6 +126,29 @@ TYPE_MAP: dict[DataType, type] = {
98
126
  }
99
127
 
100
128
 
129
+ ENTRY_PARSERS: dict[str, list[Callable[[Path], Any]]] = {
130
+ "structures": [ase.io.read],
131
+ }
132
+
133
+
134
+ def structure_ingest_wrapper(entry, prop_defs=None, prefix=None): # type: ignore
135
+ return Structure.ingest_from(entry)
136
+
137
+
138
+ OPTIMADE_CONVERTERS: dict[
139
+ str,
140
+ list[
141
+ Callable[
142
+ [Any, list[PropertyDefinition] | None, str | None], EntryResource | dict
143
+ ]
144
+ ],
145
+ ] = {
146
+ "structures": [structure_ingest_wrapper],
147
+ }
148
+
149
+ #### Wrappers for third-party parsers and converters, such as pymatgen ####
150
+
151
+
101
152
  def wrapped_json_parser(parser):
102
153
  """This wrapper allows `from_dict` parser functions to be called
103
154
  on a single JSON file.
@@ -133,20 +184,9 @@ def wrapped_json_parser(parser):
133
184
  return _wrapped_json_parser
134
185
 
135
186
 
136
- ENTRY_PARSERS: dict[str, list[Callable[[Path], Any]]] = {
137
- "structures": [
138
- ase.io.read,
139
- wrapped_json_parser(
140
- pymatgen.entries.computed_entries.ComputedStructureEntry.from_dict
141
- ),
142
- wrapped_json_parser(pymatgen.core.Structure.from_dict),
143
- ],
144
- }
145
-
146
-
147
187
  def convert_pymatgen_computed_structure_entry(
148
- pmg_entry: ComputedStructureEntry,
149
- properties: list[PropertyDefinition] | None = None,
188
+ pmg_entry,
189
+ prop_defs: list[PropertyDefinition] | None = None,
150
190
  prefix: str | None = None,
151
191
  ) -> dict:
152
192
  """Convert a pymatgen ComputedStructureEntry to an OPTIMADE EntryResource."""
@@ -159,7 +199,7 @@ def convert_pymatgen_computed_structure_entry(
159
199
  entry["id"] = id
160
200
  break
161
201
 
162
- for p in properties or []:
202
+ for p in prop_defs or []:
163
203
  # loop through any property aliases, saving the value if found and only checking
164
204
  # the real name if not
165
205
  for alias in p.aliases or []:
@@ -172,17 +212,28 @@ def convert_pymatgen_computed_structure_entry(
172
212
  return entry
173
213
 
174
214
 
175
- def structure_ingest_wrapper(entry, properties=None, prefix=None): # type: ignore
176
- return Structure.ingest_from(entry)
215
+ def add_pymatgen_parsers():
216
+ try:
217
+ import pymatgen.core
218
+ import pymatgen.entries.computed_entries
177
219
 
220
+ ENTRY_PARSERS["structures"].extend(
221
+ [
222
+ wrapped_json_parser(
223
+ pymatgen.entries.computed_entries.ComputedStructureEntry.from_dict
224
+ ),
225
+ wrapped_json_parser(pymatgen.core.Structure.from_dict),
226
+ ]
227
+ )
178
228
 
179
- OPTIMADE_CONVERTERS: dict[
180
- str,
181
- list[
182
- Callable[
183
- [Any, list[PropertyDefinition] | None, str | None], EntryResource | dict
184
- ]
185
- ],
186
- ] = {
187
- "structures": [structure_ingest_wrapper, convert_pymatgen_computed_structure_entry],
188
- }
229
+ OPTIMADE_CONVERTERS["structures"].append(
230
+ convert_pymatgen_computed_structure_entry
231
+ )
232
+ except ImportError:
233
+ warnings.warn(
234
+ "pymatgen is not installed, so corresponding parsers and converters will not be available. "
235
+ "To use these, install the 'pymatgen' extra."
236
+ )
237
+
238
+
239
+ add_pymatgen_parsers()
@@ -287,11 +287,12 @@ class OptimakeServer:
287
287
  LOGGER.info("Using the MongoMock backend.")
288
288
  if skip_mock:
289
289
  return
290
- # Importing optimade python tools loads the config and creates the mongomock
291
- # client that we need to populate.
292
- from optimade.server.entry_collections.mongo import CLIENT
290
+ # Get the MongoMock client from optimade-python-tools
291
+ from optimade.server.config import ServerConfig
292
+ from optimade.server.entry_collections.mongo import get_mongo_client
293
293
 
294
- mongo_db = CLIENT[db_name]
294
+ client = get_mongo_client(ServerConfig())
295
+ mongo_db = client[db_name]
295
296
  else:
296
297
  raise ValueError(
297
298
  f"Unknown database backend '{db_backend}'. "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optimade-maker
3
- Version: 0.7.2
3
+ Version: 0.9.0
4
4
  Summary: Tools for making OPTIMADE APIs from raw structural data.
5
5
  License: MIT
6
6
  Keywords: optimade,jsonapi,materials
@@ -15,30 +15,27 @@ Classifier: Topic :: Scientific/Engineering
15
15
  Requires-Python: <3.13,>=3.10
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
- Requires-Dist: pydantic~=2.2
19
- Requires-Dist: optimade[server]~=1.3.1
20
- Requires-Dist: pymongo
18
+ Requires-Dist: pydantic<3,>=2
19
+ Requires-Dist: optimade[server]~=1.4
20
+ Requires-Dist: pymongo<5,>=4
21
21
  Requires-Dist: pyyaml~=6.0
22
22
  Requires-Dist: tqdm~=4.65
23
23
  Requires-Dist: requests~=2.31
24
- Requires-Dist: numpy<3,>=1.22
25
24
  Requires-Dist: click~=8.1
26
- Provides-Extra: ase
27
- Requires-Dist: ase~=3.22; extra == "ase"
25
+ Requires-Dist: pandas<3,>=1.5
26
+ Requires-Dist: ase~=3.22
28
27
  Provides-Extra: pymatgen
29
28
  Requires-Dist: pymatgen>=2023.9; extra == "pymatgen"
30
- Provides-Extra: pandas
31
- Requires-Dist: pandas<3,>=1.5; extra == "pandas"
32
29
  Provides-Extra: aiida
33
- Requires-Dist: aiida-core>=2.6.3; extra == "aiida"
34
- Provides-Extra: core
35
- Requires-Dist: optimade-maker[ase,pandas,pymatgen]; extra == "core"
30
+ Requires-Dist: aiida-core~=2.6; extra == "aiida"
36
31
  Provides-Extra: ingest
37
- Requires-Dist: optimade-maker[aiida,ase,pandas,pymatgen]; extra == "ingest"
32
+ Requires-Dist: optimade-maker[aiida,pymatgen]; extra == "ingest"
38
33
  Provides-Extra: tests
39
34
  Requires-Dist: pytest~=8.3; extra == "tests"
40
35
  Requires-Dist: pytest-cov~=6.0; extra == "tests"
36
+ Requires-Dist: numpy<3,>=1.22; extra == "tests"
41
37
  Provides-Extra: dev
38
+ Requires-Dist: optimade-maker[tests]; extra == "dev"
42
39
  Requires-Dist: ruff; extra == "dev"
43
40
  Requires-Dist: pre-commit; extra == "dev"
44
41
  Requires-Dist: mypy; extra == "dev"
@@ -75,6 +72,8 @@ This repository contains the src/optimade-maker Python package and the correspon
75
72
  Install with
76
73
 
77
74
  ```bash
75
+ pip install optimade-maker
76
+ # or to get all ingestion plugins (e.g. pymatgen, aiida):
78
77
  pip install optimade-maker[ingest]
79
78
  ```
80
79
 
@@ -106,8 +105,8 @@ entries:
106
105
  - file: properties.csv
107
106
  property_definitions:
108
107
  - name: energy
109
- title: Total energy per atom
110
- description: DFT total energy per atom
108
+ title: Total energy
109
+ description: DFT total energy
111
110
  unit: eV/atom
112
111
  type: float
113
112
  ```