diff --git a/latest/CHANGELOG/CHANGELOG.md b/latest/CHANGELOG/CHANGELOG.md index d78619d..2ebe027 100644 --- a/latest/CHANGELOG/CHANGELOG.md +++ b/latest/CHANGELOG/CHANGELOG.md @@ -1,13 +1,5 @@ # Changelog -## [Unreleased](https://github.com/SINTEF/oteapi-optimade/tree/HEAD) - -[Full Changelog](https://github.com/SINTEF/oteapi-optimade/compare/v0.4.1...HEAD) - -**Fixed bugs:** - -- OPTIMADE plugin produces empty instances of http://onto-ns.com/meta/1.0/OPTIMADEStructureSpecies [\#162](https://github.com/SINTEF/oteapi-optimade/issues/162) - ## [v0.4.1](https://github.com/SINTEF/oteapi-optimade/tree/v0.4.1) (2023-10-26) [Full Changelog](https://github.com/SINTEF/oteapi-optimade/compare/v0.4.0...v0.4.1) @@ -15,6 +7,7 @@ **Fixed bugs:** - Invalid use of logging [\#174](https://github.com/SINTEF/oteapi-optimade/issues/174) +- OPTIMADE plugin produces empty instances of http://onto-ns.com/meta/1.0/OPTIMADEStructureSpecies [\#162](https://github.com/SINTEF/oteapi-optimade/issues/162) **Merged pull requests:** diff --git a/latest/CHANGELOG/index.html b/latest/CHANGELOG/index.html index ee7fdb5..d0f5db3 100644 --- a/latest/CHANGELOG/index.html +++ b/latest/CHANGELOG/index.html @@ -420,13 +420,6 @@
Fixed bugs:
-Fixed bugs:
Merged pull requests:
{ - "c2469867-d0a3-4531-b13c-939706e3138b": { + "653cd8e7-27e7-40d4-b398-40ceed2a7b1a": { "meta": "http://onto-ns.com/meta/0.1/Collection", "dimensions": { "nrelations": 60 }, "properties": { - "relations": [["mp-1033911", "_is-a", "Instance"], ["mp-1033911", "_has-uuid", "d8e52b5c-ffac-4ce7-a6b3-5e8abbda868c"], ["mp-1033911", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-757887", "_is-a", "Instance"], ["mp-757887", "_has-uuid", "41533e1a-fd71-490e-b880-7e2f7faba0cf"], ["mp-757887", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1219366", "_is-a", "Instance"], ["mp-1219366", "_has-uuid", "d256ad04-24c2-46e9-85d9-b4a7f1216e3c"], ["mp-1219366", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-733539", "_is-a", "Instance"], ["mp-733539", "_has-uuid", "2c487685-85a5-49cf-bc65-7f9250226d8a"], ["mp-733539", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-542090", "_is-a", "Instance"], ["mp-542090", "_has-uuid", "c29ce187-2ea2-4054-a7f4-f7fe38146160"], ["mp-542090", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-758465", "_is-a", "Instance"], ["mp-758465", "_has-uuid", "86963bbc-bea6-4f9f-86d4-05d9f1009be1"], ["mp-758465", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-560675", "_is-a", "Instance"], ["mp-560675", "_has-uuid", "152f9f71-0d4a-4719-9cad-1fc07c9e32e4"], ["mp-560675", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-774171", "_is-a", "Instance"], ["mp-774171", "_has-uuid", "f02b3a60-0991-4f63-966a-47bce4ee568b"], ["mp-774171", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1304778", "_is-a", "Instance"], ["mp-1304778", "_has-uuid", "d0a7119f-c457-4547-8faf-dee63a3a28b0"], ["mp-1304778", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1016821", "_is-a", "Instance"], ["mp-1016821", "_has-uuid", "41b1d16a-dc3e-48d1-9123-cf31f4ea0556"], ["mp-1016821", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1363556", "_is-a", "Instance"], ["mp-1363556", "_has-uuid", "96eb3a9d-aa81-4ab7-92a2-526d3b270c6c"], ["mp-1363556", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-757013", "_is-a", "Instance"], ["mp-757013", "_has-uuid", "7a3772db-ea1a-4971-95e7-298f373c7b7f"], ["mp-757013", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-683953", "_is-a", "Instance"], ["mp-683953", "_has-uuid", "498f0cea-fcff-4752-ac69-0b9133475d7a"], ["mp-683953", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1020609", "_is-a", "Instance"], ["mp-1020609", "_has-uuid", "57181f0d-229a-4ac5-8840-d1205862f577"], ["mp-1020609", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-17612", "_is-a", "Instance"], ["mp-17612", "_has-uuid", "4d7ab767-25d5-4267-87e1-14355d6c1b6f"], ["mp-17612", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-558129", "_is-a", "Instance"], ["mp-558129", "_has-uuid", "713fe9b3-2ac8-46df-b9f1-285ccac296f9"], ["mp-558129", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1210628", "_is-a", "Instance"], ["mp-1210628", "_has-uuid", "1da03470-55d7-48bd-89a5-a41fe271e63a"], ["mp-1210628", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1197149", "_is-a", "Instance"], ["mp-1197149", "_has-uuid", "4ac79d7e-c033-4f7a-9fad-846c6a1f1d41"], ["mp-1197149", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-21791", "_is-a", "Instance"], ["mp-21791", "_has-uuid", "a67456d3-f3be-4500-b860-cff4706bce00"], ["mp-21791", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-752892", "_is-a", "Instance"], ["mp-752892", "_has-uuid", "12441aec-e99f-4e80-b923-9fd8ad0a641b"], ["mp-752892", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"]] + "relations": [["mp-1033911", "_is-a", "Instance"], ["mp-1033911", "_has-uuid", "1aa1630b-bf79-4710-a5f2-545d21ac7701"], ["mp-1033911", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-757887", "_is-a", "Instance"], ["mp-757887", "_has-uuid", "c7a38ce8-8211-4e42-a0fa-8c4b16cea42d"], ["mp-757887", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1219366", "_is-a", "Instance"], ["mp-1219366", "_has-uuid", "692d0cdd-6b3a-49eb-b13e-b8a45c786e77"], ["mp-1219366", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-733539", "_is-a", "Instance"], ["mp-733539", "_has-uuid", "100ec662-11cc-432c-9bae-0055afbe6c24"], ["mp-733539", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-542090", "_is-a", "Instance"], ["mp-542090", "_has-uuid", "d4fd483e-5de2-46c9-beba-8edf9ba030c9"], ["mp-542090", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-758465", "_is-a", "Instance"], ["mp-758465", "_has-uuid", "2455b57d-8a46-4938-8dd0-a3afd2aa6f11"], ["mp-758465", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-560675", "_is-a", "Instance"], ["mp-560675", "_has-uuid", "b1876370-f51e-44c8-825c-8fa9df304ed6"], ["mp-560675", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-774171", "_is-a", "Instance"], ["mp-774171", "_has-uuid", "89ce9205-abb3-4605-b96a-9a419d85bf76"], ["mp-774171", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1304778", "_is-a", "Instance"], ["mp-1304778", "_has-uuid", "88e6f9b0-f495-41f6-9b78-7d9d23f7f144"], ["mp-1304778", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1016821", "_is-a", "Instance"], ["mp-1016821", "_has-uuid", "bb3b0b09-ca8b-40e7-a258-bc77948a249a"], ["mp-1016821", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1363556", "_is-a", "Instance"], ["mp-1363556", "_has-uuid", "e3399e4d-925c-4916-b4e2-300ed9a16d1a"], ["mp-1363556", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-757013", "_is-a", "Instance"], ["mp-757013", "_has-uuid", "eab02f6c-fb69-4a5e-9072-1400bfabe1bb"], ["mp-757013", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-683953", "_is-a", "Instance"], ["mp-683953", "_has-uuid", "72f75039-bbcc-4e10-a78d-ed1caefff2ad"], ["mp-683953", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1020609", "_is-a", "Instance"], ["mp-1020609", "_has-uuid", "0d529c7a-0881-4eee-b1ba-94b35e92cc2e"], ["mp-1020609", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-17612", "_is-a", "Instance"], ["mp-17612", "_has-uuid", "55aa5aa7-0eff-4013-a2db-d84b8e97bf38"], ["mp-17612", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-558129", "_is-a", "Instance"], ["mp-558129", "_has-uuid", "74f8f088-4c73-4ea8-b338-24ebbd1ac824"], ["mp-558129", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1210628", "_is-a", "Instance"], ["mp-1210628", "_has-uuid", "ca59b8b4-23c3-4af8-9ebd-fa14025e75e4"], ["mp-1210628", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-1197149", "_is-a", "Instance"], ["mp-1197149", "_has-uuid", "28dbe1d9-91bd-45fc-9278-0047cf5d3253"], ["mp-1197149", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-21791", "_is-a", "Instance"], ["mp-21791", "_has-uuid", "5fcad93f-4187-480f-b58b-992580f12955"], ["mp-21791", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"], ["mp-752892", "_is-a", "Instance"], ["mp-752892", "_has-uuid", "fe36f224-77e6-4415-90a7-2ffd12aed1d4"], ["mp-752892", "_has-meta", "http://onto-ns.com/meta/1.0/OPTIMADEStructure"]] } } } @@ -1841,241 +1841,241 @@Setup, execute and inspect the p
{ - "d8e52b5c-ffac-4ce7-a6b3-5e8abbda868c": { + "1aa1630b-bf79-4710-a5f2-545d21ac7701": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "3990d434-82ae-4451-a97f-2e66e9ac3aab", + "attributes": "38c3b251-a12b-4697-84cf-ab4586558c3b", "id": "mp-1033911" } } } { - "41533e1a-fd71-490e-b880-7e2f7faba0cf": { + "c7a38ce8-8211-4e42-a0fa-8c4b16cea42d": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "cdfb1f36-eea7-49c8-901f-63c1784d30f9", + "attributes": "3df086c2-1750-4cbd-8309-74c0389b5e78", "id": "mp-757887" } } } { - "d256ad04-24c2-46e9-85d9-b4a7f1216e3c": { + "692d0cdd-6b3a-49eb-b13e-b8a45c786e77": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "704c7790-ce6d-48ec-9fe8-f9498255a494", + "attributes": "38eaf31f-adf9-4164-9250-82b2e1308177", "id": "mp-1219366" } } } { - "2c487685-85a5-49cf-bc65-7f9250226d8a": { + "100ec662-11cc-432c-9bae-0055afbe6c24": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "cfe3d459-1961-408c-b039-d13378366f13", + "attributes": "a8458d8c-657d-4db3-868d-7f5825ed5dd5", "id": "mp-733539" } } } { - "c29ce187-2ea2-4054-a7f4-f7fe38146160": { + "d4fd483e-5de2-46c9-beba-8edf9ba030c9": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "08c02f72-682b-4798-8010-c4c693778c9d", + "attributes": "68ac43eb-dd22-441b-a49e-5bf1cb7b5411", "id": "mp-542090" } } } { - "86963bbc-bea6-4f9f-86d4-05d9f1009be1": { + "2455b57d-8a46-4938-8dd0-a3afd2aa6f11": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "53e7e2d7-88db-482b-ab0f-4ff680507718", + "attributes": "cdb8c464-8d61-4ab1-bbb0-dea798fcbc72", "id": "mp-758465" } } } { - "152f9f71-0d4a-4719-9cad-1fc07c9e32e4": { + "b1876370-f51e-44c8-825c-8fa9df304ed6": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "124d794e-a280-41f5-850d-16d91f455fb8", + "attributes": "b21284fc-16d3-46d5-8e33-ce9245edf281", "id": "mp-560675" } } } { - "f02b3a60-0991-4f63-966a-47bce4ee568b": { + "89ce9205-abb3-4605-b96a-9a419d85bf76": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "297c07ab-d5fb-4354-862d-46447490502b", + "attributes": "7b6c4566-4143-481c-84df-55fe86c88693", "id": "mp-774171" } } } { - "d0a7119f-c457-4547-8faf-dee63a3a28b0": { + "88e6f9b0-f495-41f6-9b78-7d9d23f7f144": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "81cb7e0b-e9f4-42f7-875d-633040c23326", + "attributes": "475507e4-0dc0-49a9-a806-2c633e92d525", "id": "mp-1304778" } } } { - "41b1d16a-dc3e-48d1-9123-cf31f4ea0556": { + "bb3b0b09-ca8b-40e7-a258-bc77948a249a": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "3456a598-3c51-403f-80c2-fe3b91ce1c5b", + "attributes": "5ddfaffa-72ff-4683-84d1-5a32b9539f31", "id": "mp-1016821" } } } { - "96eb3a9d-aa81-4ab7-92a2-526d3b270c6c": { + "e3399e4d-925c-4916-b4e2-300ed9a16d1a": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "c20e7851-c8bf-458e-91cd-d7ae229b280b", + "attributes": "0051bbb8-293f-46e5-8e67-20b177914e61", "id": "mp-1363556" } } } { - "7a3772db-ea1a-4971-95e7-298f373c7b7f": { + "eab02f6c-fb69-4a5e-9072-1400bfabe1bb": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "3a5a032b-ba28-4782-8fd5-6f3751a817c8", + "attributes": "6b46eaf4-f633-42c2-ad86-2ba7c7e956e4", "id": "mp-757013" } } } { - "498f0cea-fcff-4752-ac69-0b9133475d7a": { + "72f75039-bbcc-4e10-a78d-ed1caefff2ad": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "ad1863d8-7edc-4957-b0ef-250f11fe1f96", + "attributes": "1677897b-b5c5-40fa-be81-ac67cbe848ae", "id": "mp-683953" } } } { - "57181f0d-229a-4ac5-8840-d1205862f577": { + "0d529c7a-0881-4eee-b1ba-94b35e92cc2e": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "b1dbd5f6-c5cd-4ff2-8cb6-e1e904a04957", + "attributes": "b4957da4-3897-4438-aa89-7840637fff13", "id": "mp-1020609" } } } { - "4d7ab767-25d5-4267-87e1-14355d6c1b6f": { + "55aa5aa7-0eff-4013-a2db-d84b8e97bf38": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "60c8b965-de2c-4019-a2f5-635d6cca82bd", + "attributes": "0b4756e2-6179-4e68-9534-990d2c2282dd", "id": "mp-17612" } } } { - "713fe9b3-2ac8-46df-b9f1-285ccac296f9": { + "74f8f088-4c73-4ea8-b338-24ebbd1ac824": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "34c17beb-8f1e-4da6-b7c2-d3e98b3ef71e", + "attributes": "52f85e16-89ad-43cf-a42e-2adedf84e32c", "id": "mp-558129" } } } { - "1da03470-55d7-48bd-89a5-a41fe271e63a": { + "ca59b8b4-23c3-4af8-9ebd-fa14025e75e4": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "e99bdb75-0fba-4bbc-9ae8-ff1f01f6879f", + "attributes": "3a2c4cf1-9c75-4b36-9b70-72e4d67d794f", "id": "mp-1210628" } } } { - "4ac79d7e-c033-4f7a-9fad-846c6a1f1d41": { + "28dbe1d9-91bd-45fc-9278-0047cf5d3253": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "77cf62ea-8a77-4d50-a123-d8db5b400fe2", + "attributes": "bf76d6b4-4b3c-48b0-b194-52bb4e6b7688", "id": "mp-1197149" } } } { - "a67456d3-f3be-4500-b860-cff4706bce00": { + "5fcad93f-4187-480f-b58b-992580f12955": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "bac7a055-d075-406a-8da3-16ced4010d44", + "attributes": "5c82ae72-f56b-459a-8c8c-be3c4e5d162d", "id": "mp-21791" } } } { - "12441aec-e99f-4e80-b923-9fd8ad0a641b": { + "fe36f224-77e6-4415-90a7-2ffd12aed1d4": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructure", "dimensions": { }, "properties": { "type": "structures", - "attributes": "22549275-83e7-4fb6-a05f-6b29e512cbef", + "attributes": "a46e0ac1-a3c8-4d0a-86d1-b1951ba570bc", "id": "mp-752892" } } @@ -2132,7 +2132,7 @@Setup, execute and inspect the p
{ - "3990d434-82ae-4451-a97f-2e66e9ac3aab": { + "38c3b251-a12b-4697-84cf-ab4586558c3b": { "meta": "http://onto-ns.com/meta/1.0/OPTIMADEStructureAttributes", "dimensions": { "nelements": 4, @@ -2152,7 +2152,7 @@Setup, execute and inspect the p "nperiodic_dimensions": 3, "lattice_vectors": [[8.59318, 0, 0], [0, 8.59318, 0], [0, 0, 4.41201]], "cartesian_site_positions": [[0, 0, 0], [0, 4.29659, 0], [4.29659, 0, 0], [0, 2.1438, 2.20601], [0, 6.44938, 2.20601], [4.29659, 2.13079, 2.20601], [4.29659, 6.46238, 2.20601], [2.1438, 0, 2.20601], [2.13079, 4.29659, 2.20601], [6.44938, 0, 2.20601], [6.46238, 4.29659, 2.20601], [2.12673, 2.12673, 0], [2.12673, 6.46645, 0], [6.46645, 2.12673, 0], [6.46645, 6.46645, 0], [4.29659, 4.29659, 0], [2.37689, 0, 0], [2.32997, 4.29659, 0], [6.21628, 0, 0], [6.2632, 4.29659, 0], [2.16162, 2.16162, 2.20601], [2.16162, 6.43155, 2.20601], [6.43155, 2.16162, 2.20601], [6.43155, 6.43155, 2.20601], [0, 0, 2.20601], [0, 4.29659, 2.20601], [4.29659, 0, 2.20601], [4.29659, 4.29659, 2.20601], [0, 2.37689, 0], [0, 6.21628, 0], [4.29659, 2.32997, 0], [4.29659, 6.2632, 0]], - "species": ["86fdf62d-a887-4314-a168-b925ef1585da", "61325ec0-c046-43c9-89af-89b9903d9a80", "14f54cf4-e9e1-4c0a-8449-fedf91920163", "afd06f26-0e2d-481f-9a55-3cac841dd66f"], + "species": ["5d30b3fb-580f-4d79-a8db-30a1790e6ccf", "7da719f3-8bb1-4c43-a772-a86042faa374", "bb91d74f-891b-495a-8611-82ceec92e66b", "2c69e6d7-b7fe-4e23-bf6e-c9477447d7e9"], "species_at_sites": ["K", "Mg", "Mg", "Mg", "Mg", "Mg", "Mg", "Mg", "Mg", "Mg", "Mg", "Mg", "Mg", "Mg", "Mg", "Si", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O", "O"], "assemblies": null, "structure_features": [], diff --git a/latest/search/search_index.json b/latest/search/search_index.json index 10b9963..48593a5 100644 --- a/latest/search/search_index.json +++ b/latest/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"OTE-API OPTIMADE","text":"
An OTE-API Plugin with OTE strategies.
Further reading:
"},{"location":"#license-and-copyright","title":"License and copyright","text":"
- OTE-API Core Documentation
- OTE-API Services Documentation
OTE-API OPTIMADE is released under the MIT license with copyright \u00a9 SINTEF.
"},{"location":"#acknowledgment","title":"Acknowledgment","text":"OTE-API OPTIMADE has been created via the cookiecutter template for OTE-API plugins.
OTE-API OPTIMADE has been supported by the following projects:
"},{"location":"CHANGELOG/","title":"Changelog","text":""},{"location":"CHANGELOG/#unreleased","title":"Unreleased","text":"
OntoTrans (2020-2024) that receives funding from the European Union\u2019s Horizon 2020 Research and Innovation Programme, under Grant Agreement n. 862136.
VIPCOAT (2021-2025) receives funding from the European Union\u2019s Horizon 2020 Research and Innovation Programme - DT-NMBP-11-2020 Open Innovation Platform for Materials Modelling, under Grant Agreement no: 952903.
OpenModel (2021-2025) receives funding from the European Union\u2019s Horizon 2020 Research and Innovation Programme - DT-NMBP-11-2020 Open Innovation Platform for Materials Modelling, under Grant Agreement no: 953167.
Full Changelog
Fixed bugs:
"},{"location":"CHANGELOG/#v041-2023-10-26","title":"v0.4.1 (2023-10-26)","text":"
- OPTIMADE plugin produces empty instances of http://onto-ns.com/meta/1.0/OPTIMADEStructureSpecies #162
Full Changelog
Fixed bugs:
- Invalid use of logging #174
Merged pull requests:
"},{"location":"CHANGELOG/#v040-2023-10-23","title":"v0.4.0 (2023-10-23)","text":"
- Properly create assemblies and species #172 (CasperWA)
- Proper use of logging #171 (jesper-friis)
Full Changelog
Implemented enhancements:
- Add example(s) #124
Fixed bugs:
- Wrong OPTIMADEStructureAttributes datamodel #164
- Pipeline figure not being shown in docs #144
- Updated DLite installation pathway #136
Segmentation fault
from dlite in CI #115- init file missing in the new
dlite
module #113Closed issues:
- Make the JSON-serialisation of entities human readable #160
- Use ruff instead of pylint (and isort) #156
Merged pull requests:
"},{"location":"CHANGELOG/#v030-2023-03-30","title":"v0.3.0 (2023-03-30)","text":"
- Write \u00c5ngstr\u00f6m such that it is understandable by Pint in datamodel #170 (jesper-friis)
- Update data models #169 (CasperWA)
- Move from pylint (& isort) to ruff #157 (CasperWA)
- Use relative link, which works only in production #145 (CasperWA)
- Avoid DLite v0.4.0 #139 (CasperWA)
- DLite notebook example #127 (CasperWA)
- Add example to documentation #125 (CasperWA)
- Add __init__ file to dlite submodule #122 (CasperWA)
- Avoid psycopg2-binary v2.9.6 #117 (CasperWA)
Full Changelog
Implemented enhancements:
- Use
SINTEF/ci-cd
CI - Tests workflow #71- Implement support for DLite #31
Fixed bugs:
- Fix CI/CD workflows for external usage #84
- Update to
SINTEF/ci-cd
instead ofCasperWA/ci-cd
#72Closed issues:
- Use SINTEF/ci-cd v2 #104
- Reinstate pre-commit hooks for docs #68
Merged pull requests:
"},{"location":"CHANGELOG/#v022-2022-07-06","title":"v0.2.2 (2022-07-06)","text":"
- Support DLite #109 (CasperWA)
- Update to SINTEF/ci-cd v2 #105 (CasperWA)
- Update input keywords for SINTEF/ci-cd workflows #85 (CasperWA)
- Use CasperWA/ci-cd pre-commit hooks #69 (CasperWA)
Full Changelog
Implemented enhancements:
- Update to use all workflows from CasperWA/gh-actions #63 (CasperWA)
Fixed bugs:
- New workflow is removing API reference in documentation #64
Closed issues:
- Update to new repository name for callable workflows #66
Merged pull requests:
"},{"location":"CHANGELOG/#v021-2022-07-01","title":"v0.2.1 (2022-07-01)","text":"
- Use new repo name for callable workflows repo #67 (CasperWA)
- Properly create API reference and clean up #65 (CasperWA)
Full Changelog
Closed issues:
- Set
test: false
for publish workflow #61Merged pull requests:
"},{"location":"CHANGELOG/#v021-alpha1-2022-07-01","title":"v0.2.1-alpha.1 (2022-07-01)","text":"
- Don't run publish workflow as a test #62 (CasperWA)
Full Changelog
Implemented enhancements:
- Auto-merge generated PR from new workflow #49
- Properly update dependencies #46
- Use CasperWA/gh-actions workflows #60 (CasperWA)
Fixed bugs:
- New workflow failing #48
Merged pull requests:
"},{"location":"CHANGELOG/#v020-2022-05-18","title":"v0.2.0 (2022-05-18)","text":"
- [Auto-generated] Update dependencies #53 (TEAM4-0)
- [Auto-generated] Update dependencies #52 (TEAM4-0)
- Auto-merge new CD workflow-generated PR #50 (CasperWA)
- New CD workflow to update dependencies in pyproject.toml #47 (CasperWA)
- [Auto-generated] Update dependencies #44 (TEAM4-0)
Full Changelog
Implemented enhancements:
- Implement OPTIMADE filter strategy #4
Fixed bugs:
- CI docker connection issues #34
Closed issues:
- Use the
optimade
container image in CI #41- Extend acknowledgements in README #38
Merged pull requests:
"},{"location":"CHANGELOG/#v010-2022-03-29","title":"v0.1.0 (2022-03-29)","text":"
- Use the optimade container image in CI #42 (CasperWA)
- [Auto-generated] Update dependencies #40 (TEAM4-0)
- Add VIPCOAT and OpenModel to README ack #39 (CasperWA)
- Fix real backend CI job #37 (CasperWA)
- [Auto-generated] Update dependencies #36 (TEAM4-0)
- Add a Filter strategy #33 (CasperWA)
Full Changelog
Implemented enhancements:
- Correctly handle trailing slashes (
/
) #28Merged pull requests:
"},{"location":"CHANGELOG/#v002-2022-03-29","title":"v0.0.2 (2022-03-29)","text":"
- Trailing slash in base URL #29 (CasperWA)
Full Changelog
Implemented enhancements:
- Implement OPTIMADE parse strategy #5
- Implement OPTIMADE resource strategy #3
Fixed bugs:
- Fix CI connection refusal for pytest-real-backend job #26
- CD workflow failing - flit not building #23
- Black issue with click #21
- CD workflow failing #18
- GH GraphQL type issue in auto-merge workflow #6
- Fix CI #1
Closed issues:
- CI test with end-to-end #17
Merged pull requests:
- Fix pytest-real-backend CI job #27 (CasperWA)
- Test release workflow #25 (CasperWA)
- Build package prior to polluting git tree #24 (CasperWA)
- Update pre-commit hooks #22 (CasperWA)
- Fix failing release workflow #20 (CasperWA)
- Setup CI end-to-end test #19 (CasperWA)
- [Auto-generated] Update dependencies #15 (TEAM4-0)
- [Auto-generated] Update dependencies #13 (TEAM4-0)
- Implement an OPTIMADE Resource strategy #12 (CasperWA)
- [Auto-generated] Update dependencies #11 (TEAM4-0)
- [Auto-generated] Update dependencies #10 (TEAM4-0)
- Use
ID!
type instead ofString!
#7 (CasperWA)- Fix CI and use flit #2 (CasperWA)
* This Changelog was automatically generated by github_changelog_generator
"},{"location":"LICENSE/","title":"License","text":"MIT License
Copyright (c) 2022 SINTEF
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"},{"location":"all_strategies/","title":"OTE-API OPTIMADE Strategies","text":"This page provides documentation for the
oteapi_optimade.strategies
submodule, where all the OTE-API OPTIMADE strategies are located.These strategies will be available when setting up a server in an environment with oteapi-optimade installed.
"},{"location":"all_strategies/#oteapi_optimade.strategies.filter","title":"filter
","text":"Demo filter strategy.
"},{"location":"all_strategies/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy","title":"OPTIMADEFilterStrategy
dataclass
","text":"Filter Strategy.
Implements strategies:
Source code in
(\"filterType\", \"OPTIMADE\")
(\"filterType\", \"optimade\")
(\"filterType\", \"OPTiMaDe\")
oteapi_optimade/strategies/filter.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy.get","title":"@dataclass\nclass OPTIMADEFilterStrategy:\n \"\"\"Filter Strategy.\n\n **Implements strategies**:\n\n - `(\"filterType\", \"OPTIMADE\")`\n - `(\"filterType\", \"optimade\")`\n - `(\"filterType\", \"OPTiMaDe\")`\n\n \"\"\"\n\n filter_config: OPTIMADEFilterConfig\n\n def initialize(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEFilterSession:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Configuration values, specifically URL query parameters, can be provided to the\n OPTIMADE resource strategy through this filter strategy.\n\n Workflow:\n\n 1. Compile received information.\n 2. Update session with compiled information.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEFilterSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEFilterSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEFilterSession()\n\n if session.optimade_config:\n self.filter_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_config = self.filter_config.configuration.copy()\n\n if not optimade_config.query_parameters:\n optimade_config.query_parameters = OPTIMADEQueryParameters()\n\n if self.filter_config.query:\n LOGGER.debug(\"Setting filter from query.\")\n optimade_config.query_parameters.filter = self.filter_config.query\n\n if self.filter_config.limit:\n LOGGER.debug(\"Setting page_limit from limit.\")\n optimade_config.query_parameters.page_limit = self.filter_config.limit\n\n return session.copy(\n update={\n \"optimade_config\": optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n },\n )\n\n def get(\n self,\n session: \"Optional[Dict[str, Any]]\" = None,\n ) -> SessionUpdate:\n \"\"\"Execute the strategy.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n return SessionUpdate()\n
get(self, session=None)
","text":"Execute the strategy.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/filter.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy.initialize","title":"def get(\n self,\n session: \"Optional[Dict[str, Any]]\" = None,\n) -> SessionUpdate:\n \"\"\"Execute the strategy.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n return SessionUpdate()\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Configuration values, specifically URL query parameters, can be provided to the OPTIMADE resource strategy through this filter strategy.
Workflow:
- Compile received information.
- Update session with compiled information.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary context.
None
Returns:
Type DescriptionOPTIMADEFilterSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/filter.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.parse","title":"def initialize(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEFilterSession:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Configuration values, specifically URL query parameters, can be provided to the\n OPTIMADE resource strategy through this filter strategy.\n\n Workflow:\n\n 1. Compile received information.\n 2. Update session with compiled information.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEFilterSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEFilterSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEFilterSession()\n\n if session.optimade_config:\n self.filter_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_config = self.filter_config.configuration.copy()\n\n if not optimade_config.query_parameters:\n optimade_config.query_parameters = OPTIMADEQueryParameters()\n\n if self.filter_config.query:\n LOGGER.debug(\"Setting filter from query.\")\n optimade_config.query_parameters.filter = self.filter_config.query\n\n if self.filter_config.limit:\n LOGGER.debug(\"Setting page_limit from limit.\")\n optimade_config.query_parameters.page_limit = self.filter_config.limit\n\n return session.copy(\n update={\n \"optimade_config\": optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n },\n )\n
parse
","text":"Demo strategy class for text/json.
"},{"location":"all_strategies/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy","title":"OPTIMADEParseStrategy
dataclass
","text":"Parse strategy for JSON.
Implements strategies:
Source code in
(\"mediaType\", \"application/vnd.optimade+json\")
(\"mediaType\", \"application/vnd.OPTIMADE+json\")
(\"mediaType\", \"application/vnd.OPTiMaDe+json\")
(\"mediaType\", \"application/vnd.optimade+JSON\")
(\"mediaType\", \"application/vnd.OPTIMADE+JSON\")
(\"mediaType\", \"application/vnd.OPTiMaDe+JSON\")
(\"mediaType\", \"application/vnd.optimade\")
(\"mediaType\", \"application/vnd.OPTIMADE\")
(\"mediaType\", \"application/vnd.OPTiMaDe\")
oteapi_optimade/strategies/parse.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy.get","title":"@dataclass\nclass OPTIMADEParseStrategy:\n \"\"\"Parse strategy for JSON.\n\n **Implements strategies**:\n\n - `(\"mediaType\", \"application/vnd.optimade+json\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+json\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+json\")`\n - `(\"mediaType\", \"application/vnd.optimade+JSON\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+JSON\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+JSON\")`\n - `(\"mediaType\", \"application/vnd.optimade\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe\")`\n\n \"\"\"\n\n parse_config: OPTIMADEParseConfig\n\n def initialize(self, session: \"Optional[Dict[str, Any]]\" = None) -> SessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return SessionUpdate()\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEParseSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEParseSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEParseSession()\n\n if session.optimade_config:\n self.parse_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n cache = DataCache(self.parse_config.configuration.datacache_config)\n if self.parse_config.downloadUrl in cache:\n response: \"Dict[str, Any]\" = cache.get(self.parse_config.downloadUrl)\n elif (\n self.parse_config.configuration.datacache_config.accessKey\n and self.parse_config.configuration.datacache_config.accessKey in cache\n ):\n response = cache.get(\n self.parse_config.configuration.datacache_config.accessKey\n )\n else:\n download_config = self.parse_config.copy()\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n response = {\"json\": json.loads(cache.get(session.pop(\"key\")))}\n\n if (\n not response.get(\"ok\", True)\n or (\n 200 > response.get(\"status_code\", 200)\n or response.get(\"status_code\", 200) >= 300\n )\n or \"errors\" in response.get(\"json\", {})\n ):\n # Error response\n try:\n response_object = ErrorResponse(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Could not validate an error response.\\nValidationError: \"\n \"%s\\nresponse=%r\",\n exc,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate an error response.\"\n ) from exc\n else:\n # Successful response\n response_model = self.parse_config.downloadUrl.response_model()\n if response_model:\n if not isinstance(response_model, tuple):\n response_model = (response_model,)\n for model_cls in response_model:\n try:\n response_object = model_cls(**response.get(\"json\", {}))\n except ValidationError:\n pass\n else:\n break\n else:\n LOGGER.error(\n \"Could not validate for an expected response model.\\nURL=%r\\n\"\n \"response_models=%r\\nresponse=%s\",\n self.parse_config.downloadUrl,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate for an expected response model.\"\n )\n else:\n # No \"endpoint\" or unknown\n try:\n response_object = Success(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Unknown or unparseable endpoint.\\nValidatonError: %s\\n\"\n \"URL=%r\\nendpoint=%r\\nresponse_model=%r\\nresponse=%s\",\n exc,\n self.parse_config.downloadUrl,\n self.parse_config.downloadUrl.endpoint,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Unknown or unparseable endpoint.\"\n ) from exc\n\n if self.parse_config.configuration.return_object:\n session.optimade_response_object = response_object\n else:\n session.optimade_response = model2dict(response_object)\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
get(self, session=None)
","text":"Request and parse an OPTIMADE response using OPT.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromdownloadUrl
.Workflow:
- Request OPTIMADE response.
- Parse as an OPTIMADE Python tools (OPT) pydantic response model.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEParseSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/parse.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEParseSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEParseSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEParseSession()\n\n if session.optimade_config:\n self.parse_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n cache = DataCache(self.parse_config.configuration.datacache_config)\n if self.parse_config.downloadUrl in cache:\n response: \"Dict[str, Any]\" = cache.get(self.parse_config.downloadUrl)\n elif (\n self.parse_config.configuration.datacache_config.accessKey\n and self.parse_config.configuration.datacache_config.accessKey in cache\n ):\n response = cache.get(\n self.parse_config.configuration.datacache_config.accessKey\n )\n else:\n download_config = self.parse_config.copy()\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n response = {\"json\": json.loads(cache.get(session.pop(\"key\")))}\n\n if (\n not response.get(\"ok\", True)\n or (\n 200 > response.get(\"status_code\", 200)\n or response.get(\"status_code\", 200) >= 300\n )\n or \"errors\" in response.get(\"json\", {})\n ):\n # Error response\n try:\n response_object = ErrorResponse(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Could not validate an error response.\\nValidationError: \"\n \"%s\\nresponse=%r\",\n exc,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate an error response.\"\n ) from exc\n else:\n # Successful response\n response_model = self.parse_config.downloadUrl.response_model()\n if response_model:\n if not isinstance(response_model, tuple):\n response_model = (response_model,)\n for model_cls in response_model:\n try:\n response_object = model_cls(**response.get(\"json\", {}))\n except ValidationError:\n pass\n else:\n break\n else:\n LOGGER.error(\n \"Could not validate for an expected response model.\\nURL=%r\\n\"\n \"response_models=%r\\nresponse=%s\",\n self.parse_config.downloadUrl,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate for an expected response model.\"\n )\n else:\n # No \"endpoint\" or unknown\n try:\n response_object = Success(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Unknown or unparseable endpoint.\\nValidatonError: %s\\n\"\n \"URL=%r\\nendpoint=%r\\nresponse_model=%r\\nresponse=%s\",\n exc,\n self.parse_config.downloadUrl,\n self.parse_config.downloadUrl.endpoint,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Unknown or unparseable endpoint.\"\n ) from exc\n\n if self.parse_config.configuration.return_object:\n session.optimade_response_object = response_object\n else:\n session.optimade_response = model2dict(response_object)\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/parse.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource","title":"def initialize(self, session: \"Optional[Dict[str, Any]]\" = None) -> SessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return SessionUpdate()\n
resource
","text":"OPTIMADE resource strategy.
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy","title":"OPTIMADEResourceStrategy
dataclass
","text":"OPTIMADE Resource Strategy.
Implements strategies:
Source code in
(\"accessService\", \"optimade\")
(\"accessService\", \"OPTIMADE\")
(\"accessService\", \"OPTiMaDe\")
(\"accessService\", \"optimade+dlite\")
(\"accessService\", \"OPTIMADE+dlite\")
(\"accessService\", \"OPTiMaDe+dlite\")
(\"accessService\", \"optimade+DLite\")
(\"accessService\", \"OPTIMADE+DLite\")
(\"accessService\", \"OPTiMaDe+DLite\")
oteapi_optimade/strategies/resource.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy.get","title":"@dataclass\nclass OPTIMADEResourceStrategy:\n \"\"\"OPTIMADE Resource Strategy.\n\n **Implements strategies**:\n\n - `(\"accessService\", \"optimade\")`\n - `(\"accessService\", \"OPTIMADE\")`\n - `(\"accessService\", \"OPTiMaDe\")`\n - `(\"accessService\", \"optimade+dlite\")`\n - `(\"accessService\", \"OPTIMADE+dlite\")`\n - `(\"accessService\", \"OPTiMaDe+dlite\")`\n - `(\"accessService\", \"optimade+DLite\")`\n - `(\"accessService\", \"OPTIMADE+DLite\")`\n - `(\"accessService\", \"OPTiMaDe+DLite\")`\n\n \"\"\"\n\n resource_config: OPTIMADEResourceConfig\n\n def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n ) -> \"Union[SessionUpdate, DLiteSessionUpdate]\":\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n ):\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n return SessionUpdate()\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEResourceSession:\n \"\"\"Execute an OPTIMADE query to `accessUrl`.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `accessUrl`.\n\n Workflow:\n 1. Update configuration according to session.\n 2. Deconstruct `accessUrl` (done partly by\n `oteapi_optimade.models.custom_types.OPTIMADEUrl`).\n 3. Reconstruct the complete query URL.\n 4. Send query.\n 5. Store result in data cache.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEResourceSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEResourceSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEResourceSession()\n\n if session.optimade_config:\n self.resource_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_endpoint = self.resource_config.accessUrl.endpoint or \"structures\"\n optimade_query = (\n self.resource_config.configuration.query_parameters\n or OPTIMADEQueryParameters()\n )\n LOGGER.debug(\"resource_config: %r\", self.resource_config)\n\n if self.resource_config.accessUrl.query:\n parsed_query = parse_qs(self.resource_config.accessUrl.query)\n for field, value in parsed_query.items():\n # Only use the latest defined value for any parameter\n if field not in optimade_query.__fields_set__:\n LOGGER.debug(\n \"Setting %r from accessUrl (value=%r)\", field, value[-1]\n )\n setattr(optimade_query, field, value[-1])\n\n LOGGER.debug(\"optimade_query after update: %r\", optimade_query)\n\n optimade_url = OPTIMADEUrl(\n f\"{self.resource_config.accessUrl.base_url}\"\n f\"/{self.resource_config.accessUrl.version or 'v1'}\"\n f\"/{optimade_endpoint}?{optimade_query.generate_query_string()}\"\n )\n LOGGER.debug(\"OPTIMADE URL to be requested: %s\", optimade_url)\n\n # Set cache access key to the full OPTIMADE URL.\n self.resource_config.configuration.datacache_config.accessKey = optimade_url\n\n # Perform query\n response = requests.get(\n optimade_url,\n allow_redirects=True,\n timeout=(3, 27), # timeout in seconds (connect, read)\n )\n\n if optimade_query.response_format and optimade_query.response_format != \"json\":\n raise NotImplementedError(\n \"Can only handle JSON responses for now. Requested response format: \"\n f\"{optimade_query.response_format!r}\"\n )\n\n cache = DataCache(config=self.resource_config.configuration.datacache_config)\n cache.add(\n {\n \"status_code\": response.status_code,\n \"ok\": response.ok,\n \"json\": response.json(),\n }\n )\n\n parse_with_dlite = use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n )\n\n parse_mediaType = (\n \"application/vnd.\"\n f\"{self.resource_config.accessService.split('+', maxsplit=1)[0]}\"\n )\n if parse_with_dlite:\n parse_mediaType += \"+DLite\"\n elif optimade_query.response_format:\n parse_mediaType += f\"+{optimade_query.response_format}\"\n\n parse_config = {\n \"downloadUrl\": optimade_url,\n \"mediaType\": parse_mediaType,\n \"configuration\": {\n \"datacache_config\": self.resource_config.configuration.datacache_config,\n \"return_object\": True,\n },\n }\n\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the session.\"\n )\n optimade_response: \"OPTIMADEResponse\" = session.pop(\"optimade_response_object\")\n if \"optimade_response\" in session and not session.get(\"optimade_response\"):\n del session[\"optimade_response\"]\n\n if isinstance(optimade_response, ErrorResponse):\n optimade_resources = optimade_response.errors\n session.optimade_resource_model = (\n f\"{OptimadeError.__module__}:OptimadeError\"\n )\n elif isinstance(optimade_response, ReferenceResponseMany):\n optimade_resources = [\n Reference(entry).as_dict\n if isinstance(entry, dict)\n else Reference(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, ReferenceResponseOne):\n optimade_resources = [\n Reference(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Reference(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, StructureResponseMany):\n optimade_resources = [\n Structure(entry).as_dict\n if isinstance(entry, dict)\n else Structure(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n elif isinstance(optimade_response, StructureResponseOne):\n optimade_resources = [\n Structure(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Structure(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n else:\n LOGGER.debug(\n \"Could not parse response as errors, references or structures. \"\n \"Response:\\n%r\",\n optimade_response,\n )\n raise OPTIMADEParseError(\n \"Could not retrieve errors, references or structures from response \"\n f\"from {optimade_url}. It could be a valid OPTIMADE API response, \"\n \"however it may not be supported by OTEAPI-OPTIMADE. It may also be an \"\n \"invalid response completely.\"\n )\n\n session.optimade_resources = [\n model2dict(resource) for resource in optimade_resources\n ]\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
get(self, session=None)
","text":"Execute an OPTIMADE query to
accessUrl
.This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromaccessUrl
.Workflow: 1. Update configuration according to session. 2. Deconstruct
accessUrl
(done partly byoteapi_optimade.models.custom_types.OPTIMADEUrl
). 3. Reconstruct the complete query URL. 4. Send query. 5. Store result in data cache.Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEResourceSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEResourceSession:\n \"\"\"Execute an OPTIMADE query to `accessUrl`.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `accessUrl`.\n\n Workflow:\n 1. Update configuration according to session.\n 2. Deconstruct `accessUrl` (done partly by\n `oteapi_optimade.models.custom_types.OPTIMADEUrl`).\n 3. Reconstruct the complete query URL.\n 4. Send query.\n 5. Store result in data cache.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEResourceSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEResourceSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEResourceSession()\n\n if session.optimade_config:\n self.resource_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_endpoint = self.resource_config.accessUrl.endpoint or \"structures\"\n optimade_query = (\n self.resource_config.configuration.query_parameters\n or OPTIMADEQueryParameters()\n )\n LOGGER.debug(\"resource_config: %r\", self.resource_config)\n\n if self.resource_config.accessUrl.query:\n parsed_query = parse_qs(self.resource_config.accessUrl.query)\n for field, value in parsed_query.items():\n # Only use the latest defined value for any parameter\n if field not in optimade_query.__fields_set__:\n LOGGER.debug(\n \"Setting %r from accessUrl (value=%r)\", field, value[-1]\n )\n setattr(optimade_query, field, value[-1])\n\n LOGGER.debug(\"optimade_query after update: %r\", optimade_query)\n\n optimade_url = OPTIMADEUrl(\n f\"{self.resource_config.accessUrl.base_url}\"\n f\"/{self.resource_config.accessUrl.version or 'v1'}\"\n f\"/{optimade_endpoint}?{optimade_query.generate_query_string()}\"\n )\n LOGGER.debug(\"OPTIMADE URL to be requested: %s\", optimade_url)\n\n # Set cache access key to the full OPTIMADE URL.\n self.resource_config.configuration.datacache_config.accessKey = optimade_url\n\n # Perform query\n response = requests.get(\n optimade_url,\n allow_redirects=True,\n timeout=(3, 27), # timeout in seconds (connect, read)\n )\n\n if optimade_query.response_format and optimade_query.response_format != \"json\":\n raise NotImplementedError(\n \"Can only handle JSON responses for now. Requested response format: \"\n f\"{optimade_query.response_format!r}\"\n )\n\n cache = DataCache(config=self.resource_config.configuration.datacache_config)\n cache.add(\n {\n \"status_code\": response.status_code,\n \"ok\": response.ok,\n \"json\": response.json(),\n }\n )\n\n parse_with_dlite = use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n )\n\n parse_mediaType = (\n \"application/vnd.\"\n f\"{self.resource_config.accessService.split('+', maxsplit=1)[0]}\"\n )\n if parse_with_dlite:\n parse_mediaType += \"+DLite\"\n elif optimade_query.response_format:\n parse_mediaType += f\"+{optimade_query.response_format}\"\n\n parse_config = {\n \"downloadUrl\": optimade_url,\n \"mediaType\": parse_mediaType,\n \"configuration\": {\n \"datacache_config\": self.resource_config.configuration.datacache_config,\n \"return_object\": True,\n },\n }\n\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the session.\"\n )\n optimade_response: \"OPTIMADEResponse\" = session.pop(\"optimade_response_object\")\n if \"optimade_response\" in session and not session.get(\"optimade_response\"):\n del session[\"optimade_response\"]\n\n if isinstance(optimade_response, ErrorResponse):\n optimade_resources = optimade_response.errors\n session.optimade_resource_model = (\n f\"{OptimadeError.__module__}:OptimadeError\"\n )\n elif isinstance(optimade_response, ReferenceResponseMany):\n optimade_resources = [\n Reference(entry).as_dict\n if isinstance(entry, dict)\n else Reference(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, ReferenceResponseOne):\n optimade_resources = [\n Reference(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Reference(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, StructureResponseMany):\n optimade_resources = [\n Structure(entry).as_dict\n if isinstance(entry, dict)\n else Structure(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n elif isinstance(optimade_response, StructureResponseOne):\n optimade_resources = [\n Structure(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Structure(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n else:\n LOGGER.debug(\n \"Could not parse response as errors, references or structures. \"\n \"Response:\\n%r\",\n optimade_response,\n )\n raise OPTIMADEParseError(\n \"Could not retrieve errors, references or structures from response \"\n f\"from {optimade_url}. It could be a valid OPTIMADE API response, \"\n \"however it may not be supported by OTEAPI-OPTIMADE. It may also be an \"\n \"invalid response completely.\"\n )\n\n session.optimade_resources = [\n model2dict(resource) for resource in optimade_resources\n ]\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionUnion[SessionUpdate, DLiteSessionUpdate]
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource.use_dlite","title":"def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n) -> \"Union[SessionUpdate, DLiteSessionUpdate]\":\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n ):\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n return SessionUpdate()\n
use_dlite(access_service, use_dlite_flag)
","text":"Determine whether DLite should be utilized in the Resource strategy.
Parameters:
Name Type Description Defaultaccess_service
str
The accessService value from the resource's configuration.
requireduse_dlite_flag
bool
The strategy-specific
requireduse_dlite
configuration option.Returns:
Type Descriptionbool
Based on the accessService value, then whether DLite should be used or not.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"api_reference/exceptions/","title":"exceptions","text":"def use_dlite(access_service: str, use_dlite_flag: bool) -> bool:\n \"\"\"Determine whether DLite should be utilized in the Resource strategy.\n\n Parameters:\n access_service: The accessService value from the resource's configuration.\n use_dlite_flag: The strategy-specific `use_dlite` configuration option.\n\n Returns:\n Based on the accessService value, then whether DLite should be used or not.\n\n \"\"\"\n if (\n any(dlite_form in access_service for dlite_form in [\"DLite\", \"dlite\"])\n or use_dlite_flag\n ):\n if oteapi_dlite_version is None:\n raise MissingDependency(\n \"OTEAPI-DLite is not found on the system. This is required to use \"\n \"DLite with the OTEAPI-OPTIMADE strategies.\"\n )\n return True\n return False\n
OTE-API OPTIMADE-specific Python exceptions.
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.BaseOteapiOptimadeException","title":"BaseOteapiOptimadeException (Exception)
","text":"Base OTE-API OPTIMADE exception.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.ConfigurationError","title":"class BaseOteapiOptimadeException(Exception):\n \"\"\"Base OTE-API OPTIMADE exception.\"\"\"\n
ConfigurationError (BaseOteapiOptimadeException)
","text":"An error occurred when dealing with strategy configurations.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.MissingDependency","title":"class ConfigurationError(BaseOteapiOptimadeException):\n \"\"\"An error occurred when dealing with strategy configurations.\"\"\"\n
MissingDependency (BaseOteapiOptimadeException)
","text":"A required dependency is missing.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.OPTIMADEParseError","title":"class MissingDependency(BaseOteapiOptimadeException):\n \"\"\"A required dependency is missing.\"\"\"\n
OPTIMADEParseError (BaseOteapiOptimadeException)
","text":"Could not use OPTIMADE Python tools to parse an OPTIMADE API response.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.OPTIMADEResponseError","title":"class OPTIMADEParseError(BaseOteapiOptimadeException):\n \"\"\"Could not use OPTIMADE Python tools to parse an OPTIMADE API response.\"\"\"\n
OPTIMADEResponseError (RequestError)
","text":"An OPTIMADE error was returned from a URL request.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.RequestError","title":"class OPTIMADEResponseError(RequestError):\n \"\"\"An OPTIMADE error was returned from a URL request.\"\"\"\n
RequestError (BaseOteapiOptimadeException)
","text":"A general error occured when performing a URL request.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/utils/","title":"utils","text":"class RequestError(BaseOteapiOptimadeException):\n \"\"\"A general error occured when performing a URL request.\"\"\"\n
Utility functions to be used in OTEAPI OPTIMADE.
"},{"location":"api_reference/utils/#oteapi_optimade.utils.model2dict","title":"model2dict(model, **dict_kwargs)
","text":"Convert a pydantic model to a Python dictionary.
This works similarly to the
dict()
method for pydantic models, but ensures any and all nested pydantic models are also converted to dictionaries.Parameters:
Name Type Description Defaultmodel
Union[BaseModel, dict[str, Any]]
The pydantic model or Python dictionary to be converted fully to a Python dictionary, through and through.
required**dict_kwargs
Dict[Any, Any]
Keyword arguments to be passed to
dict()
method calls for pydantic models. Note, this will be used for alldict()
method calls.{}
Returns:
Type Descriptiondict[str, Any]
A Python dictionary, where all nested values that were pydantic models are also converted to Python dictionaries.
Source code inoteapi_optimade/utils.py
"},{"location":"api_reference/dlite/parse/","title":"parse","text":"def model2dict(\n model: \"Union[BaseModel, dict[str, Any]]\", **dict_kwargs: \"Any\"\n) -> \"dict[str, Any]\":\n \"\"\"Convert a pydantic model to a Python dictionary.\n\n This works similarly to the `dict()` method for pydantic models, but ensures any\n and all nested pydantic models are also converted to dictionaries.\n\n Parameters:\n model: The pydantic model or Python dictionary to be converted fully to a\n Python dictionary, through and through.\n **dict_kwargs (Dict[Any, Any]): Keyword arguments to be passed to `dict()`\n method calls for pydantic models.\n Note, this will be used for _all_ `dict()` method calls.\n\n Returns:\n A Python dictionary, where all nested values that were pydantic models are also\n converted to Python dictionaries.\n\n \"\"\"\n\n def _internal(model_: \"Any\") -> \"Any\":\n \"\"\"Internal function to be used recursively.\"\"\"\n if isinstance(model_, dict):\n return {key: _internal(value) for key, value in model_.items()}\n if isinstance(model_, Iterable) and not isinstance(model_, (bytes, str)):\n return type(model_)(_internal(value) for value in model_) # type: ignore[call-arg]\n if isinstance(model_, BaseModel):\n return _internal(model_.dict(**dict_kwargs))\n return model_\n\n if isinstance(model, BaseModel):\n res = model.dict(**dict_kwargs)\n elif isinstance(model, dict):\n res = deepcopy(model)\n else:\n raise TypeError(\"model must be either a pydantic model or a dict.\")\n\n return _internal(res)\n
OTEAPI strategy for parsing OPTIMADE structure resources to DLite instances.
"},{"location":"api_reference/dlite/parse/#oteapi_optimade.dlite.parse.OPTIMADEDLiteParseStrategy","title":"OPTIMADEDLiteParseStrategy
dataclass
","text":"Parse strategy for JSON.
Implements strategies:
Source code in
(\"mediaType\", \"application/vnd.optimade+dlite\")
(\"mediaType\", \"application/vnd.OPTIMADE+dlite\")
(\"mediaType\", \"application/vnd.OPTiMaDe+dlite\")
(\"mediaType\", \"application/vnd.optimade+DLite\")
(\"mediaType\", \"application/vnd.OPTIMADE+DLite\")
(\"mediaType\", \"application/vnd.OPTiMaDe+DLite\")
oteapi_optimade/dlite/parse.py
"},{"location":"api_reference/dlite/parse/#oteapi_optimade.dlite.parse.OPTIMADEDLiteParseStrategy.get","title":"@dataclass\nclass OPTIMADEDLiteParseStrategy:\n \"\"\"Parse strategy for JSON.\n\n **Implements strategies**:\n\n - `(\"mediaType\", \"application/vnd.optimade+dlite\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+dlite\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+dlite\")`\n - `(\"mediaType\", \"application/vnd.optimade+DLite\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+DLite\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+DLite\")`\n\n \"\"\"\n\n parse_config: OPTIMADEDLiteParseConfig\n\n def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n ) -> DLiteSessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n ---\n\n The OPTIMADE Structure needs to be parsed into DLite instances inside-out,\n meaning the most nested data structures must first be parsed, and then the ones\n 1 layer up and so on until the most upper layer can be parsed.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n session = OPTIMADEParseStrategy(self.parse_config).get(session)\n\n entities_path = Path(__file__).resolve().parent.resolve() / \"entities\"\n\n dlite.storage_path.append(str(entities_path / \"*.yaml\"))\n\n # JSONAPIResourceLinks = dlite.Instance.from_url(\n # f\"yaml://{entities_path}/JSONAPIResourceLinks.yaml\"\n # )\n OPTIMADEStructure = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructure.yaml\"\n )\n OPTIMADEStructureAssembly = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureAssembly.yaml\"\n )\n OPTIMADEStructureAttributes = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureAttributes.yaml\"\n )\n OPTIMADEStructureSpecies = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureSpecies.yaml\"\n )\n\n if self.parse_config.configuration.return_object:\n # The response is given as a \"proper\" pydantic data model instance\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the \"\n \"session.\"\n )\n\n # Currently, only \"structures\" entries are supported and handled\n if isinstance(session.optimade_response_object, StructureResponseMany):\n structures = [\n Structure(entry)\n if isinstance(entry, dict)\n else Structure(entry.dict())\n for entry in session.optimade_response_object.data\n ]\n elif isinstance(session.optimade_response_object, StructureResponseOne):\n structures = [\n Structure(session.optimade_response_object.data)\n if isinstance(session.optimade_response_object.data, dict)\n else Structure(session.optimade_response_object.data.dict())\n ]\n elif isinstance(session.optimade_response_object, Success):\n if isinstance(session.optimade_response_object.data, dict):\n structures = [Structure(session.optimade_response_object.data)]\n elif isinstance(session.optimade_response_object.data, BaseModel):\n structures = [\n Structure(session.optimade_response_object.data.dict())\n ]\n elif isinstance(session.optimade_response_object.data, list):\n structures = [\n Structure(entry)\n if isinstance(entry, dict)\n else Structure(entry.dict())\n for entry in session.optimade_response_object.data\n ]\n else:\n LOGGER.debug(\n \"Could not determine what to do with `data`. Type %s.\",\n type(session.optimade_response_object.data),\n )\n raise OPTIMADEParseError(\n \"Could not parse `data` entry in response.\"\n )\n else:\n LOGGER.debug(\n \"Got currently unsupported response type %s. Only structures are \"\n \"supported.\",\n session.optimade_response_object.__class__.__name__,\n )\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n )\n else:\n # The response is given as pure Python dictionary\n\n if \"optimade_response\" not in session:\n raise ValueError(\n \"'optimade_response' was expected to be present in the session.\"\n )\n\n if not session.optimade_response or \"data\" not in session.optimade_response:\n LOGGER.debug(\"Not a successful response - no 'data' entry found.\")\n return session\n\n if isinstance(session.optimade_response[\"data\"], list):\n try:\n structures = [\n Structure(entry) for entry in session.optimade_response[\"data\"]\n ]\n except ValidationError as exc:\n LOGGER.debug(\n \"Could not parse list of 'data' entries as structures.\"\n )\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n ) from exc\n elif session.optimade_response is not None:\n try:\n structures = [Structure(session.optimade_response[\"data\"])]\n except ValidationError as exc:\n LOGGER.debug(\"Could not parse single 'data' entry as a structure.\")\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n ) from exc\n else:\n LOGGER.debug(\"Could not parse 'data' entries as structures.\")\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n )\n\n dlite_collection = get_collection(session)\n\n # DLite-fy OPTIMADE structures\n for structure in structures:\n new_structure_attributes: dict[str, \"Any\"] = {}\n\n # Most inner layer: assemblies & species\n if structure.attributes.assemblies:\n # Non-zero length list of assemblies (which could be a list of dicts or\n # a list of pydantic models)\n\n new_structure_attributes[\"assemblies\"] = []\n\n for assembly in structure.attributes.assemblies:\n # Ensure we're dealing with a normal Python dict\n assembly = (\n assembly.dict(exclude_none=True)\n if isinstance(assembly, BaseModel)\n else assembly\n )\n\n dimensions = {\n \"ngroups\": len(assembly.get(\"group_probabilities\", []) or []),\n \"nsites\": len(assembly.get(\"sites_in_groups\", []) or []),\n }\n new_structure_attributes[\"assemblies\"].append(\n OPTIMADEStructureAssembly(\n dimensions=dimensions, properties=assembly\n )\n )\n\n if structure.attributes.species:\n # Non-zero length list of species (which could be a list of dicts or a\n # list of pydantic models)\n\n new_structure_attributes[\"species\"] = []\n\n for species_individual in structure.attributes.species:\n # Ensure we're dealing with a normal Python dict\n species_individual = (\n species_individual.dict(exclude_none=True)\n if isinstance(species_individual, BaseModel)\n else species_individual\n )\n\n dimensions = {\n \"nelements\": len(\n species_individual.get(\"chemical_symbols\", []) or []\n ),\n \"nattached_elements\": len(\n species_individual.get(\"attached\", []) or []\n ),\n }\n new_structure_attributes[\"species\"].append(\n OPTIMADEStructureSpecies(\n dimensions=dimensions,\n properties=species_individual,\n )\n )\n\n # Attributes\n new_structure_attributes.update(\n structure.attributes.dict(\n exclude={\"species\", \"assemblies\", \"nelements\", \"nsites\"}\n )\n )\n for key in list(new_structure_attributes):\n if key.startswith(\"_\"):\n new_structure_attributes.pop(key)\n\n # Structure features values are Enum values, so we need to convert them to\n # their string (true) values\n new_structure_attributes[\"structure_features\"] = [\n _.value for _ in new_structure_attributes[\"structure_features\"]\n ]\n\n new_structure = OPTIMADEStructure(\n dimensions={},\n properties={\n \"attributes\": OPTIMADEStructureAttributes(\n dimensions={\n \"nelements\": structure.attributes.nelements or 0,\n \"dimensionality\": 3,\n \"nsites\": structure.attributes.nsites or 0,\n \"nspecies\": len(structure.attributes.species)\n if structure.attributes.species\n else 0,\n \"nstructure_features\": len(\n structure.attributes.structure_features\n ),\n },\n properties=new_structure_attributes,\n ),\n \"type\": structure.entry.type,\n \"id\": structure.entry.id,\n },\n )\n dlite_collection.add(label=structure.entry.id, inst=new_structure)\n\n update_collection(collection=dlite_collection)\n\n return session\n
get(self, session=None)
","text":"Request and parse an OPTIMADE response using OPT.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromdownloadUrl
.Workflow:
- Request OPTIMADE response.
- Parse as an OPTIMADE Python tools (OPT) pydantic response model.
The OPTIMADE Structure needs to be parsed into DLite instances inside-out, meaning the most nested data structures must first be parsed, and then the ones 1 layer up and so on until the most upper layer can be parsed.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEParseSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/dlite/parse.py
"},{"location":"api_reference/dlite/parse/#oteapi_optimade.dlite.parse.OPTIMADEDLiteParseStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n ---\n\n The OPTIMADE Structure needs to be parsed into DLite instances inside-out,\n meaning the most nested data structures must first be parsed, and then the ones\n 1 layer up and so on until the most upper layer can be parsed.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n session = OPTIMADEParseStrategy(self.parse_config).get(session)\n\n entities_path = Path(__file__).resolve().parent.resolve() / \"entities\"\n\n dlite.storage_path.append(str(entities_path / \"*.yaml\"))\n\n # JSONAPIResourceLinks = dlite.Instance.from_url(\n # f\"yaml://{entities_path}/JSONAPIResourceLinks.yaml\"\n # )\n OPTIMADEStructure = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructure.yaml\"\n )\n OPTIMADEStructureAssembly = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureAssembly.yaml\"\n )\n OPTIMADEStructureAttributes = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureAttributes.yaml\"\n )\n OPTIMADEStructureSpecies = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureSpecies.yaml\"\n )\n\n if self.parse_config.configuration.return_object:\n # The response is given as a \"proper\" pydantic data model instance\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the \"\n \"session.\"\n )\n\n # Currently, only \"structures\" entries are supported and handled\n if isinstance(session.optimade_response_object, StructureResponseMany):\n structures = [\n Structure(entry)\n if isinstance(entry, dict)\n else Structure(entry.dict())\n for entry in session.optimade_response_object.data\n ]\n elif isinstance(session.optimade_response_object, StructureResponseOne):\n structures = [\n Structure(session.optimade_response_object.data)\n if isinstance(session.optimade_response_object.data, dict)\n else Structure(session.optimade_response_object.data.dict())\n ]\n elif isinstance(session.optimade_response_object, Success):\n if isinstance(session.optimade_response_object.data, dict):\n structures = [Structure(session.optimade_response_object.data)]\n elif isinstance(session.optimade_response_object.data, BaseModel):\n structures = [\n Structure(session.optimade_response_object.data.dict())\n ]\n elif isinstance(session.optimade_response_object.data, list):\n structures = [\n Structure(entry)\n if isinstance(entry, dict)\n else Structure(entry.dict())\n for entry in session.optimade_response_object.data\n ]\n else:\n LOGGER.debug(\n \"Could not determine what to do with `data`. Type %s.\",\n type(session.optimade_response_object.data),\n )\n raise OPTIMADEParseError(\n \"Could not parse `data` entry in response.\"\n )\n else:\n LOGGER.debug(\n \"Got currently unsupported response type %s. Only structures are \"\n \"supported.\",\n session.optimade_response_object.__class__.__name__,\n )\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n )\n else:\n # The response is given as pure Python dictionary\n\n if \"optimade_response\" not in session:\n raise ValueError(\n \"'optimade_response' was expected to be present in the session.\"\n )\n\n if not session.optimade_response or \"data\" not in session.optimade_response:\n LOGGER.debug(\"Not a successful response - no 'data' entry found.\")\n return session\n\n if isinstance(session.optimade_response[\"data\"], list):\n try:\n structures = [\n Structure(entry) for entry in session.optimade_response[\"data\"]\n ]\n except ValidationError as exc:\n LOGGER.debug(\n \"Could not parse list of 'data' entries as structures.\"\n )\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n ) from exc\n elif session.optimade_response is not None:\n try:\n structures = [Structure(session.optimade_response[\"data\"])]\n except ValidationError as exc:\n LOGGER.debug(\"Could not parse single 'data' entry as a structure.\")\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n ) from exc\n else:\n LOGGER.debug(\"Could not parse 'data' entries as structures.\")\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n )\n\n dlite_collection = get_collection(session)\n\n # DLite-fy OPTIMADE structures\n for structure in structures:\n new_structure_attributes: dict[str, \"Any\"] = {}\n\n # Most inner layer: assemblies & species\n if structure.attributes.assemblies:\n # Non-zero length list of assemblies (which could be a list of dicts or\n # a list of pydantic models)\n\n new_structure_attributes[\"assemblies\"] = []\n\n for assembly in structure.attributes.assemblies:\n # Ensure we're dealing with a normal Python dict\n assembly = (\n assembly.dict(exclude_none=True)\n if isinstance(assembly, BaseModel)\n else assembly\n )\n\n dimensions = {\n \"ngroups\": len(assembly.get(\"group_probabilities\", []) or []),\n \"nsites\": len(assembly.get(\"sites_in_groups\", []) or []),\n }\n new_structure_attributes[\"assemblies\"].append(\n OPTIMADEStructureAssembly(\n dimensions=dimensions, properties=assembly\n )\n )\n\n if structure.attributes.species:\n # Non-zero length list of species (which could be a list of dicts or a\n # list of pydantic models)\n\n new_structure_attributes[\"species\"] = []\n\n for species_individual in structure.attributes.species:\n # Ensure we're dealing with a normal Python dict\n species_individual = (\n species_individual.dict(exclude_none=True)\n if isinstance(species_individual, BaseModel)\n else species_individual\n )\n\n dimensions = {\n \"nelements\": len(\n species_individual.get(\"chemical_symbols\", []) or []\n ),\n \"nattached_elements\": len(\n species_individual.get(\"attached\", []) or []\n ),\n }\n new_structure_attributes[\"species\"].append(\n OPTIMADEStructureSpecies(\n dimensions=dimensions,\n properties=species_individual,\n )\n )\n\n # Attributes\n new_structure_attributes.update(\n structure.attributes.dict(\n exclude={\"species\", \"assemblies\", \"nelements\", \"nsites\"}\n )\n )\n for key in list(new_structure_attributes):\n if key.startswith(\"_\"):\n new_structure_attributes.pop(key)\n\n # Structure features values are Enum values, so we need to convert them to\n # their string (true) values\n new_structure_attributes[\"structure_features\"] = [\n _.value for _ in new_structure_attributes[\"structure_features\"]\n ]\n\n new_structure = OPTIMADEStructure(\n dimensions={},\n properties={\n \"attributes\": OPTIMADEStructureAttributes(\n dimensions={\n \"nelements\": structure.attributes.nelements or 0,\n \"dimensionality\": 3,\n \"nsites\": structure.attributes.nsites or 0,\n \"nspecies\": len(structure.attributes.species)\n if structure.attributes.species\n else 0,\n \"nstructure_features\": len(\n structure.attributes.structure_features\n ),\n },\n properties=new_structure_attributes,\n ),\n \"type\": structure.entry.type,\n \"id\": structure.entry.id,\n },\n )\n dlite_collection.add(label=structure.entry.id, inst=new_structure)\n\n update_collection(collection=dlite_collection)\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionDLiteSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/dlite/parse.py
"},{"location":"api_reference/models/config/","title":"config","text":"def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n) -> DLiteSessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n
General OPTIMADE configuration models.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.DEFAULT_CACHE_CONFIG_VALUES","title":"DEFAULT_CACHE_CONFIG_VALUES
","text":"Set the
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig","title":"expireTime
andtag
to default values for the data cache.OPTIMADEConfig (AttrDict)
pydantic-model
","text":"OPTIMADE configuration.
Source code inoteapi_optimade/models/config.py
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.datacache_config","title":"class OPTIMADEConfig(AttrDict):\n \"\"\"OPTIMADE configuration.\"\"\"\n\n version: str = Field(\n \"v1\",\n description=\"The version part of the OPTIMADE versioned base URL.\",\n regex=r\"^v[0-9]+(\\.[0-9]+){,2}$\",\n )\n endpoint: Literal[\"references\", \"structures\"] = Field(\n \"structures\",\n description=\"Supported OPTIMADE entry resource endpoint.\",\n )\n query_parameters: Optional[OPTIMADEQueryParameters] = Field(\n None,\n description=\"URL query parameters to be used in the OPTIMADE query.\",\n )\n datacache_config: DataCacheConfig = Field(\n DataCacheConfig(**DEFAULT_CACHE_CONFIG_VALUES),\n description=\"Configuration options for the local data cache.\",\n )\n return_object: bool = Field(\n False,\n description=(\n \"Whether or not to return a response object (using the pydantic model).\\n\"\n \"\\nImportant:\\n This should _only_ be used if the strategy is called \"\n \"directly and not via an OTEAPI REST API service.\"\n ),\n )\n use_dlite: bool = Field(\n False,\n description=\"Whether or not to store the results in a DLite Collection.\",\n )\n\n @validator(\"datacache_config\")\n def default_datacache_config(cls, value: DataCacheConfig) -> DataCacheConfig:\n \"\"\"Use default values for `DataCacheConfig` if not supplied.\"\"\"\n original_set_values = len(value.__fields_set__)\n\n for field, default_value in DEFAULT_CACHE_CONFIG_VALUES.items():\n if field in value.__fields_set__:\n # Use the set value instead of the default\n continue\n setattr(value, field, default_value)\n\n if len(value.__fields_set__) > original_set_values:\n # Re-validate model and return it\n return value.validate(\n {\n field: field_value\n for field, field_value in value.dict().items()\n if field in value.__fields_set__\n }\n )\n return value\n
datacache_config: DataCacheConfig
pydantic-field
","text":"Configuration options for the local data cache.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.endpoint","title":"endpoint: Literal['references', 'structures']
pydantic-field
","text":"Supported OPTIMADE entry resource endpoint.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.query_parameters","title":"query_parameters: OPTIMADEQueryParameters
pydantic-field
","text":"URL query parameters to be used in the OPTIMADE query.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.return_object","title":"return_object: bool
pydantic-field
","text":"Whether or not to return a response object (using the pydantic model).
Important
This should only be used if the strategy is called directly and not via an OTEAPI REST API service.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.use_dlite","title":"use_dlite: bool
pydantic-field
","text":"Whether or not to store the results in a DLite Collection.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.version","title":"version: ConstrainedStrValue
pydantic-field
","text":"The version part of the OPTIMADE versioned base URL.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.default_datacache_config","title":"default_datacache_config(value)
classmethod
","text":"Use default values for
Source code inDataCacheConfig
if not supplied.oteapi_optimade/models/config.py
"},{"location":"api_reference/models/custom_types/","title":"custom_types","text":"@validator(\"datacache_config\")\ndef default_datacache_config(cls, value: DataCacheConfig) -> DataCacheConfig:\n \"\"\"Use default values for `DataCacheConfig` if not supplied.\"\"\"\n original_set_values = len(value.__fields_set__)\n\n for field, default_value in DEFAULT_CACHE_CONFIG_VALUES.items():\n if field in value.__fields_set__:\n # Use the set value instead of the default\n continue\n setattr(value, field, default_value)\n\n if len(value.__fields_set__) > original_set_values:\n # Re-validate model and return it\n return value.validate(\n {\n field: field_value\n for field, field_value in value.dict().items()\n if field in value.__fields_set__\n }\n )\n return value\n
Custom \"pydantic\" types used in OTEAPI-OPTIMADE.
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.LOGGER","title":"LOGGER
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl","title":"OPTIMADEUrl (str)
","text":"A deconstructed OPTIMADE URL.
An OPTIMADE URL is made up in the following way:
<BASE URL>/[<VERSION>/]<ENDPOINT>?<QUERY PARAMETERS>\n
Where parts in square brackets (
Source code in[]
) are optional.oteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.allowed_schemes","title":"class OPTIMADEUrl(str):\n \"\"\"A deconstructed OPTIMADE URL.\n\n An OPTIMADE URL is made up in the following way:\n\n <BASE URL>/[<VERSION>/]<ENDPOINT>?<QUERY PARAMETERS>\n\n Where parts in square brackets (`[]`) are optional.\n \"\"\"\n\n strip_whitespace = True\n min_length = 1\n # https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers\n max_length = 2083\n allowed_schemes = {\"http\", \"https\"}\n tld_required = False\n user_required = False\n\n __slots__ = (\n \"base_url\",\n \"version\",\n \"endpoint\",\n \"query\",\n \"scheme\",\n \"tld\",\n \"host_type\",\n )\n\n @no_type_check\n def __new__(cls, url: \"Optional[str]\" = None, **kwargs) -> object:\n return str.__new__(\n cls,\n cls.build(**kwargs) if url is None else url,\n )\n\n def __init__(\n self,\n url: str,\n *,\n base_url: \"Optional[str]\" = None,\n version: \"Optional[str]\" = None,\n endpoint: \"Optional[str]\" = None,\n query: \"Optional[str]\" = None,\n scheme: \"Optional[str]\" = None,\n tld: \"Optional[str]\" = None,\n host_type: str = \"domain\",\n ) -> None:\n str.__init__(url)\n self.base_url = base_url\n self.version = version\n self.endpoint = endpoint\n self.query = query\n self.scheme = scheme\n self.tld = tld\n self.host_type = host_type\n\n @classmethod\n def build(\n cls,\n *,\n base_url: \"str\",\n version: \"Optional[str]\" = None,\n endpoint: \"Optional[str]\" = None,\n query: \"Optional[str]\" = None,\n **_kwargs: str,\n ) -> str:\n \"\"\"Build complete URL from URL parts.\"\"\"\n url = base_url.rstrip(\"/\")\n if version:\n url += f\"/{version}\"\n if endpoint:\n url += f\"/{endpoint}\"\n if query:\n url += f\"?{query}\"\n return url\n\n @classmethod\n def __modify_schema__(cls, field_schema: \"Dict[str, Any]\") -> None:\n update_not_none(\n field_schema,\n minLength=cls.min_length,\n maxLength=cls.max_length,\n format=\"uri\",\n )\n\n @classmethod\n def __get_validators__(cls) -> \"CallableGenerator\":\n yield cls.validate\n\n @staticmethod\n def urlquote_qs(url: str) -> str:\n \"\"\"Use `urllib.parse.quote` for query part of URL.\"\"\"\n parsed_url = urlparse(url)\n quoted_query = urlquote(parsed_url.query, safe=\"=&,\")\n parsed_url_list = list(parsed_url)\n parsed_url_list[-2] = quoted_query\n return urlunparse(parsed_url_list)\n\n @classmethod\n def validate(\n cls, value: \"Any\", field: \"ModelField\", config: \"BaseConfig\"\n ) -> \"OPTIMADEUrl\":\n \"\"\"Pydantic validation of an OPTIMADE URL.\"\"\"\n if value.__class__ == cls:\n return value\n\n value: str = str_validator(value)\n if cls.strip_whitespace:\n value = value.strip()\n url: str = cast(str, constr_length_validator(value, field, config))\n url = cls.urlquote_qs(url)\n\n url_match = url_regex().match(url)\n if url_match is None:\n raise ValueError(f\"Cannot match URL ({url!r}) as a valid URL.\")\n\n original_parts = cast(\"Parts\", url_match.groupdict())\n parts = cls.apply_default_parts(original_parts)\n host, tld, host_type, rebuild = cls.validate_host(parts)\n optimade_parts = cls.build_optimade_parts(parts, host)\n optimade_parts = cls.validate_parts(parts, optimade_parts)\n\n if url_match.end() != len(url):\n raise errors.UrlExtraError(extra=url[url_match.end() :])\n\n return cls(\n None if rebuild else url,\n base_url=optimade_parts[\"base_url\"],\n version=optimade_parts[\"version\"],\n endpoint=optimade_parts[\"endpoint\"],\n query=optimade_parts[\"query\"],\n scheme=parts[\"scheme\"],\n tld=tld,\n host_type=host_type,\n )\n\n @classmethod\n def validate_host(cls, parts: \"Parts\") -> \"Tuple[str, Optional[str], str, bool]\":\n \"\"\"Validate host-part of the URL.\"\"\"\n host: \"Optional[str]\" = None\n tld: \"Optional[str]\" = None\n rebuild: bool = False\n for host_type in (\"domain\", \"ipv4\", \"ipv6\"):\n host = parts[host_type] # type: ignore[literal-required]\n if host:\n break\n else:\n raise errors.UrlHostError()\n\n if host_type == \"domain\":\n is_international = False\n domain = ascii_domain_regex().fullmatch(host)\n if domain is None:\n domain = int_domain_regex().fullmatch(host)\n if domain is None:\n raise errors.UrlHostError()\n is_international = True\n\n tld = domain.group(\"tld\")\n if tld is None and not is_international:\n domain = int_domain_regex().fullmatch(host)\n if domain is None:\n raise ValueError(\"domain cannot be None\")\n tld = domain.group(\"tld\")\n is_international = True\n\n if tld is not None:\n tld = tld[1:]\n elif cls.tld_required:\n raise errors.UrlHostTldError()\n\n if is_international:\n host_type = \"int_domain\"\n rebuild = True\n host = host.encode(\"idna\").decode(\"ascii\")\n if tld is not None:\n tld = tld.encode(\"idna\").decode(\"ascii\")\n\n return host, tld, host_type, rebuild\n\n @staticmethod\n def get_default_parts(parts: \"Parts\") -> \"Parts\":\n \"\"\"Dictionary of default URL-part values.\"\"\"\n return {\"port\": \"80\" if parts[\"scheme\"] == \"http\" else \"443\"}\n\n @classmethod\n def apply_default_parts(cls, parts: \"Parts\") -> \"Parts\":\n \"\"\"Apply default URL-part values if no value is given.\"\"\"\n for key, value in cls.get_default_parts(parts).items():\n if not parts[key]: # type: ignore[literal-required]\n parts[key] = value # type: ignore[literal-required]\n return parts\n\n @classmethod\n def build_optimade_parts(cls, parts: \"Parts\", host: str) -> \"OPTIMADEParts\":\n \"\"\"Convert URL parts to equivalent OPTIMADE URL parts.\"\"\"\n base_url = f\"{parts['scheme']}://\"\n if parts[\"user\"]:\n base_url += parts[\"user\"]\n if parts[\"password\"]:\n base_url += f\":{parts['password']}\"\n if parts[\"user\"] or parts[\"password\"]:\n base_url += \"@\"\n base_url += host\n # Hide port if it's a standard HTTP (80) or HTTPS (443) port.\n if parts[\"port\"] and parts[\"port\"] not in (\"80\", \"443\"):\n base_url += f\":{parts['port']}\"\n if parts[\"path\"]:\n base_url += parts[\"path\"]\n\n base_url_match = optimade_base_url_regex().fullmatch(base_url)\n LOGGER.debug(\n \"OPTIMADE base URL regex match groups: %s\",\n base_url_match.groupdict() if base_url_match else base_url_match,\n )\n if base_url_match is None:\n raise ValueError(\n \"Could not match given string with OPTIMADE base URL regex.\"\n )\n\n endpoint_match = optimade_endpoint_regex().findall(\n base_url_match.group(\"path\") if base_url_match.group(\"path\") else \"\"\n )\n LOGGER.debug(\"OPTIMADE endpoint regex matches: %s\", endpoint_match)\n for path_version, path_endpoint in endpoint_match:\n if path_endpoint:\n break\n else:\n LOGGER.debug(\"Could not match given string with OPTIMADE endpoint regex.\")\n path_version, path_endpoint = \"\", \"\"\n\n base_url = base_url_match.group(\"base_url\")\n if path_version:\n base_url = base_url[: -(len(path_version) + len(path_endpoint) + 2)]\n elif path_endpoint:\n base_url = base_url[: -(len(path_endpoint) + 1)]\n\n optimade_parts = {\n \"base_url\": base_url.rstrip(\"/\"),\n \"version\": path_version or None,\n \"endpoint\": path_endpoint or None,\n \"query\": parts[\"query\"],\n }\n return cast(\"OPTIMADEParts\", optimade_parts)\n\n @classmethod\n def validate_parts(\n cls, parts: \"Parts\", optimade_parts: \"OPTIMADEParts\"\n ) -> \"OPTIMADEParts\":\n \"\"\"\n A method used to validate parts of an URL.\n Could be overridden to set default values for parts if missing\n \"\"\"\n scheme = parts[\"scheme\"]\n if scheme is None:\n raise errors.UrlSchemeError()\n\n if cls.allowed_schemes and scheme.lower() not in cls.allowed_schemes:\n raise errors.UrlSchemePermittedError(set(cls.allowed_schemes))\n\n port = parts[\"port\"]\n if port is not None and int(port) > 65_535:\n raise errors.UrlPortError()\n\n user = parts[\"user\"]\n if cls.user_required and user is None:\n raise errors.UrlUserInfoError()\n\n base_url = optimade_parts[\"base_url\"]\n if base_url is None:\n raise errors.UrlError()\n\n return optimade_parts\n\n def __repr__(self) -> str:\n extra = \", \".join(\n f\"{n}={getattr(self, n)!r}\"\n for n in self.__slots__\n if getattr(self, n) is not None\n )\n return f\"{self.__class__.__name__}({super().__repr__()}, {extra})\"\n\n def response_model(self) -> \"Union[Tuple[Success, ...], Success, None]\":\n \"\"\"Return the endpoint's corresponding response model (from OPT).\"\"\"\n if not self.endpoint or self.endpoint == \"versions\":\n return None\n return {\n \"info\": (InfoResponse, EntryInfoResponse),\n \"links\": LinksResponse,\n \"structures\": (StructureResponseMany, StructureResponseOne),\n \"references\": (ReferenceResponseMany, ReferenceResponseOne),\n \"calculations\": (EntryResponseMany, EntryResponseOne),\n }.get(self.endpoint, Success)\n
allowed_schemes
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.max_length","title":"max_length
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.min_length","title":"min_length
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.strip_whitespace","title":"strip_whitespace
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.tld_required","title":"tld_required
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.user_required","title":"user_required
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.__init__","title":"__init__(self, url, *, base_url=None, version=None, endpoint=None, query=None, scheme=None, tld=None, host_type='domain')
special
","text":"Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.apply_default_parts","title":"def __init__(\n self,\n url: str,\n *,\n base_url: \"Optional[str]\" = None,\n version: \"Optional[str]\" = None,\n endpoint: \"Optional[str]\" = None,\n query: \"Optional[str]\" = None,\n scheme: \"Optional[str]\" = None,\n tld: \"Optional[str]\" = None,\n host_type: str = \"domain\",\n) -> None:\n str.__init__(url)\n self.base_url = base_url\n self.version = version\n self.endpoint = endpoint\n self.query = query\n self.scheme = scheme\n self.tld = tld\n self.host_type = host_type\n
apply_default_parts(parts)
classmethod
","text":"Apply default URL-part values if no value is given.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.build","title":"@classmethod\ndef apply_default_parts(cls, parts: \"Parts\") -> \"Parts\":\n \"\"\"Apply default URL-part values if no value is given.\"\"\"\n for key, value in cls.get_default_parts(parts).items():\n if not parts[key]: # type: ignore[literal-required]\n parts[key] = value # type: ignore[literal-required]\n return parts\n
build(*, base_url, version=None, endpoint=None, query=None, **_kwargs)
classmethod
","text":"Build complete URL from URL parts.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.build_optimade_parts","title":"@classmethod\ndef build(\n cls,\n *,\n base_url: \"str\",\n version: \"Optional[str]\" = None,\n endpoint: \"Optional[str]\" = None,\n query: \"Optional[str]\" = None,\n **_kwargs: str,\n) -> str:\n \"\"\"Build complete URL from URL parts.\"\"\"\n url = base_url.rstrip(\"/\")\n if version:\n url += f\"/{version}\"\n if endpoint:\n url += f\"/{endpoint}\"\n if query:\n url += f\"?{query}\"\n return url\n
build_optimade_parts(parts, host)
classmethod
","text":"Convert URL parts to equivalent OPTIMADE URL parts.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.get_default_parts","title":"@classmethod\ndef build_optimade_parts(cls, parts: \"Parts\", host: str) -> \"OPTIMADEParts\":\n \"\"\"Convert URL parts to equivalent OPTIMADE URL parts.\"\"\"\n base_url = f\"{parts['scheme']}://\"\n if parts[\"user\"]:\n base_url += parts[\"user\"]\n if parts[\"password\"]:\n base_url += f\":{parts['password']}\"\n if parts[\"user\"] or parts[\"password\"]:\n base_url += \"@\"\n base_url += host\n # Hide port if it's a standard HTTP (80) or HTTPS (443) port.\n if parts[\"port\"] and parts[\"port\"] not in (\"80\", \"443\"):\n base_url += f\":{parts['port']}\"\n if parts[\"path\"]:\n base_url += parts[\"path\"]\n\n base_url_match = optimade_base_url_regex().fullmatch(base_url)\n LOGGER.debug(\n \"OPTIMADE base URL regex match groups: %s\",\n base_url_match.groupdict() if base_url_match else base_url_match,\n )\n if base_url_match is None:\n raise ValueError(\n \"Could not match given string with OPTIMADE base URL regex.\"\n )\n\n endpoint_match = optimade_endpoint_regex().findall(\n base_url_match.group(\"path\") if base_url_match.group(\"path\") else \"\"\n )\n LOGGER.debug(\"OPTIMADE endpoint regex matches: %s\", endpoint_match)\n for path_version, path_endpoint in endpoint_match:\n if path_endpoint:\n break\n else:\n LOGGER.debug(\"Could not match given string with OPTIMADE endpoint regex.\")\n path_version, path_endpoint = \"\", \"\"\n\n base_url = base_url_match.group(\"base_url\")\n if path_version:\n base_url = base_url[: -(len(path_version) + len(path_endpoint) + 2)]\n elif path_endpoint:\n base_url = base_url[: -(len(path_endpoint) + 1)]\n\n optimade_parts = {\n \"base_url\": base_url.rstrip(\"/\"),\n \"version\": path_version or None,\n \"endpoint\": path_endpoint or None,\n \"query\": parts[\"query\"],\n }\n return cast(\"OPTIMADEParts\", optimade_parts)\n
get_default_parts(parts)
staticmethod
","text":"Dictionary of default URL-part values.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.response_model","title":"@staticmethod\ndef get_default_parts(parts: \"Parts\") -> \"Parts\":\n \"\"\"Dictionary of default URL-part values.\"\"\"\n return {\"port\": \"80\" if parts[\"scheme\"] == \"http\" else \"443\"}\n
response_model(self)
","text":"Return the endpoint's corresponding response model (from OPT).
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.urlquote_qs","title":"def response_model(self) -> \"Union[Tuple[Success, ...], Success, None]\":\n \"\"\"Return the endpoint's corresponding response model (from OPT).\"\"\"\n if not self.endpoint or self.endpoint == \"versions\":\n return None\n return {\n \"info\": (InfoResponse, EntryInfoResponse),\n \"links\": LinksResponse,\n \"structures\": (StructureResponseMany, StructureResponseOne),\n \"references\": (ReferenceResponseMany, ReferenceResponseOne),\n \"calculations\": (EntryResponseMany, EntryResponseOne),\n }.get(self.endpoint, Success)\n
urlquote_qs(url)
staticmethod
","text":"Use
Source code inurllib.parse.quote
for query part of URL.oteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.validate","title":"@staticmethod\ndef urlquote_qs(url: str) -> str:\n \"\"\"Use `urllib.parse.quote` for query part of URL.\"\"\"\n parsed_url = urlparse(url)\n quoted_query = urlquote(parsed_url.query, safe=\"=&,\")\n parsed_url_list = list(parsed_url)\n parsed_url_list[-2] = quoted_query\n return urlunparse(parsed_url_list)\n
validate(value, field, config)
classmethod
","text":"Pydantic validation of an OPTIMADE URL.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.validate_host","title":"@classmethod\ndef validate(\n cls, value: \"Any\", field: \"ModelField\", config: \"BaseConfig\"\n) -> \"OPTIMADEUrl\":\n \"\"\"Pydantic validation of an OPTIMADE URL.\"\"\"\n if value.__class__ == cls:\n return value\n\n value: str = str_validator(value)\n if cls.strip_whitespace:\n value = value.strip()\n url: str = cast(str, constr_length_validator(value, field, config))\n url = cls.urlquote_qs(url)\n\n url_match = url_regex().match(url)\n if url_match is None:\n raise ValueError(f\"Cannot match URL ({url!r}) as a valid URL.\")\n\n original_parts = cast(\"Parts\", url_match.groupdict())\n parts = cls.apply_default_parts(original_parts)\n host, tld, host_type, rebuild = cls.validate_host(parts)\n optimade_parts = cls.build_optimade_parts(parts, host)\n optimade_parts = cls.validate_parts(parts, optimade_parts)\n\n if url_match.end() != len(url):\n raise errors.UrlExtraError(extra=url[url_match.end() :])\n\n return cls(\n None if rebuild else url,\n base_url=optimade_parts[\"base_url\"],\n version=optimade_parts[\"version\"],\n endpoint=optimade_parts[\"endpoint\"],\n query=optimade_parts[\"query\"],\n scheme=parts[\"scheme\"],\n tld=tld,\n host_type=host_type,\n )\n
validate_host(parts)
classmethod
","text":"Validate host-part of the URL.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.validate_parts","title":"@classmethod\ndef validate_host(cls, parts: \"Parts\") -> \"Tuple[str, Optional[str], str, bool]\":\n \"\"\"Validate host-part of the URL.\"\"\"\n host: \"Optional[str]\" = None\n tld: \"Optional[str]\" = None\n rebuild: bool = False\n for host_type in (\"domain\", \"ipv4\", \"ipv6\"):\n host = parts[host_type] # type: ignore[literal-required]\n if host:\n break\n else:\n raise errors.UrlHostError()\n\n if host_type == \"domain\":\n is_international = False\n domain = ascii_domain_regex().fullmatch(host)\n if domain is None:\n domain = int_domain_regex().fullmatch(host)\n if domain is None:\n raise errors.UrlHostError()\n is_international = True\n\n tld = domain.group(\"tld\")\n if tld is None and not is_international:\n domain = int_domain_regex().fullmatch(host)\n if domain is None:\n raise ValueError(\"domain cannot be None\")\n tld = domain.group(\"tld\")\n is_international = True\n\n if tld is not None:\n tld = tld[1:]\n elif cls.tld_required:\n raise errors.UrlHostTldError()\n\n if is_international:\n host_type = \"int_domain\"\n rebuild = True\n host = host.encode(\"idna\").decode(\"ascii\")\n if tld is not None:\n tld = tld.encode(\"idna\").decode(\"ascii\")\n\n return host, tld, host_type, rebuild\n
validate_parts(parts, optimade_parts)
classmethod
","text":"A method used to validate parts of an URL. Could be overridden to set default values for parts if missing
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.optimade_base_url_regex","title":"@classmethod\ndef validate_parts(\n cls, parts: \"Parts\", optimade_parts: \"OPTIMADEParts\"\n) -> \"OPTIMADEParts\":\n \"\"\"\n A method used to validate parts of an URL.\n Could be overridden to set default values for parts if missing\n \"\"\"\n scheme = parts[\"scheme\"]\n if scheme is None:\n raise errors.UrlSchemeError()\n\n if cls.allowed_schemes and scheme.lower() not in cls.allowed_schemes:\n raise errors.UrlSchemePermittedError(set(cls.allowed_schemes))\n\n port = parts[\"port\"]\n if port is not None and int(port) > 65_535:\n raise errors.UrlPortError()\n\n user = parts[\"user\"]\n if cls.user_required and user is None:\n raise errors.UrlUserInfoError()\n\n base_url = optimade_parts[\"base_url\"]\n if base_url is None:\n raise errors.UrlError()\n\n return optimade_parts\n
optimade_base_url_regex()
","text":"A regular expression for an OPTIMADE base URL.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.optimade_endpoint_regex","title":"def optimade_base_url_regex() -> \"Pattern[str]\":\n \"\"\"A regular expression for an OPTIMADE base URL.\"\"\"\n global _OPTIMADE_BASE_URL_REGEX\n if _OPTIMADE_BASE_URL_REGEX is None:\n _OPTIMADE_BASE_URL_REGEX = re.compile(\n r\"^(?P<base_url>\"\n # scheme https://tools.ietf.org/html/rfc3986#appendix-A\n r\"(?:[a-z][a-z0-9+\\-.]+://)?\"\n r\"(?:[^\\s:/]*(?::[^\\s/]*)?@)?\" # user info\n r\"(?:\"\n r\"(?:\\d{1,3}\\.){3}\\d{1,3}(?=$|[/:#?])|\" # ipv4\n r\"\\[[A-F0-9]*:[A-F0-9:]+\\](?=$|[/:#?])|\" # ipv6\n r\"[^\\s/:?#]+\" # domain, validation occurs later\n r\")?\"\n r\"(?::\\d+)?\" # port\n r\"(?P<path>/[^\\s?#]*)?\" # path\n r\")\",\n re.IGNORECASE,\n )\n return _OPTIMADE_BASE_URL_REGEX\n
optimade_endpoint_regex()
","text":"A regular expression for an OPTIMADE base URL.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/query/","title":"query","text":"def optimade_endpoint_regex() -> \"Pattern[str]\":\n \"\"\"A regular expression for an OPTIMADE base URL.\"\"\"\n global _OPTIMADE_ENDPOINT_REGEX\n if _OPTIMADE_ENDPOINT_REGEX is None:\n _OPTIMADE_ENDPOINT_REGEX = re.compile(\n # version\n r\"(?:/(?P<version>v[0-9]+(?:\\.[0-9+]){0,2})\"\n r\"(?=/info|/links|/version|/structures|/references|/calculations\"\n r\"|/extensions))?\"\n # endpoint\n r\"(?:/(?P<endpoint>(?:info|links|versions|structures|references\"\n r\"|calculations|extensions)(?:/[^\\s?#]*)?))?$\"\n )\n return _OPTIMADE_ENDPOINT_REGEX\n
Data models related to OPTIMADE queries.
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.QUERY_PARAMETERS","title":"QUERY_PARAMETERS
","text":"Entry listing URL query parameters from the
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters","title":"optimade
package (EntryListingQueryParams
).OPTIMADEQueryParameters (BaseModel)
pydantic-model
","text":"Common OPTIMADE entry listing endpoint query parameters.
Source code inoteapi_optimade/models/query.py
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.api_hint","title":"class OPTIMADEQueryParameters(BaseModel, validate_assignment=True):\n \"\"\"Common OPTIMADE entry listing endpoint query parameters.\"\"\"\n\n filter: Optional[str] = Field(\n QUERY_PARAMETERS.filter.default,\n description=QUERY_PARAMETERS.filter.description,\n )\n response_format: Optional[str] = Field(\n QUERY_PARAMETERS.response_format.default,\n description=QUERY_PARAMETERS.response_format.description,\n )\n email_address: Optional[EmailStr] = Field(\n QUERY_PARAMETERS.email_address.default,\n description=QUERY_PARAMETERS.email_address.description,\n )\n response_fields: Optional[str] = Field(\n QUERY_PARAMETERS.response_fields.default,\n description=QUERY_PARAMETERS.response_fields.description,\n regex=QUERY_PARAMETERS.response_fields.regex,\n )\n sort: Optional[str] = Field(\n QUERY_PARAMETERS.sort.default,\n description=QUERY_PARAMETERS.sort.description,\n regex=QUERY_PARAMETERS.sort.regex,\n )\n page_limit: Optional[int] = Field(\n QUERY_PARAMETERS.page_limit.default,\n description=QUERY_PARAMETERS.page_limit.description,\n ge=QUERY_PARAMETERS.page_limit.ge,\n )\n page_offset: Optional[int] = Field(\n QUERY_PARAMETERS.page_offset.default,\n description=QUERY_PARAMETERS.page_offset.description,\n ge=QUERY_PARAMETERS.page_offset.ge,\n )\n page_number: Optional[int] = Field(\n QUERY_PARAMETERS.page_number.default,\n description=QUERY_PARAMETERS.page_number.description,\n ge=QUERY_PARAMETERS.page_number.ge,\n )\n page_cursor: Optional[int] = Field(\n QUERY_PARAMETERS.page_cursor.default,\n description=QUERY_PARAMETERS.page_cursor.description,\n ge=QUERY_PARAMETERS.page_cursor.ge,\n )\n page_above: Optional[int] = Field(\n QUERY_PARAMETERS.page_above.default,\n description=QUERY_PARAMETERS.page_above.description,\n ge=QUERY_PARAMETERS.page_above.ge,\n )\n page_below: Optional[int] = Field(\n QUERY_PARAMETERS.page_below.default,\n description=QUERY_PARAMETERS.page_below.description,\n ge=QUERY_PARAMETERS.page_below.ge,\n )\n include: Optional[str] = Field(\n QUERY_PARAMETERS.include.default,\n description=QUERY_PARAMETERS.include.description,\n )\n # api_hint is not yet initialized in `EntryListingQueryParams`.\n # These values are copied verbatim from `optimade==0.16.10`.\n api_hint: Optional[str] = Field(\n \"\",\n description=(\n \"If the client provides the parameter, the value SHOULD have the format \"\n \"`vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a\"\n \" minor version of the API. For example, if a client appends \"\n \"`api_hint=v1.0` to the query string, the hint provided is for major \"\n \"version 1 and minor version 0.\"\n ),\n regex=r\"(v[0-9]+(\\.[0-9]+)?)?\",\n )\n\n def generate_query_string(self) -> str:\n \"\"\"Generate a valid URL query string based on the set fields.\"\"\"\n res = {}\n for field, value in self.dict().items():\n if value or field in self.__fields_set__:\n res[field] = unquote(value) if isinstance(value, str) else value\n return urlencode(res, quote_via=quote)\n
api_hint: ConstrainedStrValue
pydantic-field
","text":"If the client provides the parameter, the value SHOULD have the format
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.email_address","title":"vMAJOR
orvMAJOR.MINOR
, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appendsapi_hint=v1.0
to the query string, the hint provided is for major version 1 and minor version 0.email_address: EmailStr
pydantic-field
","text":"An email address of the user making the request. The email SHOULD be that of a person and not an automatic system. Example:
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.filter","title":"http://example.com/v1/structures?email_address=user@example.com
filter: str
pydantic-field
","text":"A filter string, in the format described in section API Filtering Format Specification of the specification.
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.include","title":"include: str
pydantic-field
","text":"A server MAY implement the JSON API concept of returning compound documents by utilizing the
include
query parameter as specified by JSON API 1.0.All related resource objects MUST be returned as part of an array value for the top-level
included
field, see the section JSON Response Schema: Common Fields.The value of
include
MUST be a comma-separated list of \"relationship paths\", as defined in the JSON API. If relationship paths are not supported, or a server is unable to identify a relationship path a400 Bad Request
response MUST be made.The default value for
include
isreferences
. This meansreferences
entries MUST always be included under the top-level fieldincluded
as default, since a server assumes ifinclude
is not specified by a client in the request, it is still specified asinclude=references
. Note, if a client explicitly specifiesinclude
and leaves outreferences
,references
resource objects MUST NOT be included under the top-level fieldincluded
, as per the definition ofincluded
, see section JSON Response Schema: Common Fields.Note: A query with the parameter
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_above","title":"include
set to the empty string means no related resource objects are to be returned under the top-level fieldincluded
.page_above: int
pydantic-field
","text":"RECOMMENDED for use with value-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_below","title":"page_above
/page_below
andpage_limit
is RECOMMENDED. Example: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasingid
, sopage_above
value refers to anid
value):/structures?page_above=4000&page_limit=100
.page_below: int
pydantic-field
","text":"RECOMMENDED for use with value-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_cursor","title":"page_above
/page_below
andpage_limit
is RECOMMENDED.page_cursor: ConstrainedIntValue
pydantic-field
","text":"RECOMMENDED for use with cursor-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_limit","title":"page_cursor
andpage_limit
is RECOMMENDED.page_limit: ConstrainedIntValue
pydantic-field
","text":"Sets a numerical limit on the number of entries returned. See JSON API 1.0. The API implementation MUST return no more than the number specified. It MAY return fewer. The database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned). The default limit value is up to the API implementation to decide. Example:
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_number","title":"http://example.com/optimade/v1/structures?page_limit=100
page_number: int
pydantic-field
","text":"RECOMMENDED for use with page-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_offset","title":"page_number
andpage_limit
is RECOMMENDED. It is RECOMMENDED that the first page has number 1, i.e., thatpage_number
is 1-based. Example: Fetch page 2 of up to 50 structures per page:/structures?page_number=2&page_limit=50
.page_offset: ConstrainedIntValue
pydantic-field
","text":"RECOMMENDED for use with offset-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.response_fields","title":"page_offset
andpage_limit
is RECOMMENDED. Example: Skip 50 structures and fetch up to 100:/structures?page_offset=50&page_limit=100
.response_fields: ConstrainedStrValue
pydantic-field
","text":"A comma-delimited set of fields to be provided in the output. If provided, these fields MUST be returned along with the REQUIRED fields. Other OPTIONAL fields MUST NOT be returned when this parameter is present. Example:
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.response_format","title":"http://example.com/v1/structures?response_fields=last_modified,nsites
response_format: str
pydantic-field
","text":"The output format requested (see section Response Format). Defaults to the format string 'json', which specifies the standard output format described in this specification. Example:
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.sort","title":"http://example.com/v1/structures?response_format=xml
sort: ConstrainedStrValue
pydantic-field
","text":"If supporting sortable queries, an implementation MUST use the
sort
query parameter with format as specified by JSON API 1.0.An implementation MAY support multiple sort fields for a single query. If it does, it again MUST conform to the JSON API 1.0 specification.
If an implementation supports sorting for an entry listing endpoint, then the
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.generate_query_string","title":"/info/<entries>
endpoint MUST include, for each field name<fieldname>
in itsdata.properties.<fieldname>
response value that can be used for sorting, the keysortable
with valuetrue
. If a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out thesortable
key or set it equal tofalse
for the specific field name. The set of field names, withsortable
equal totrue
are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification. The fieldsortable
is in addition to each property description and other OPTIONAL fields. An example is shown in the section Entry Listing Info Endpoints.generate_query_string(self)
","text":"Generate a valid URL query string based on the set fields.
Source code inoteapi_optimade/models/query.py
"},{"location":"api_reference/models/strategies/filter/","title":"filter","text":"def generate_query_string(self) -> str:\n \"\"\"Generate a valid URL query string based on the set fields.\"\"\"\n res = {}\n for field, value in self.dict().items():\n if value or field in self.__fields_set__:\n res[field] = unquote(value) if isinstance(value, str) else value\n return urlencode(res, quote_via=quote)\n
Models specific to the filter strategy.
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterConfig","title":"OPTIMADEFilterConfig (FilterConfig)
pydantic-model
","text":"OPTIMADE-specific filter strategy config.
Note
The
Source code incondition
parameter is not taken into account.oteapi_optimade/models/strategies/filter.py
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession","title":"class OPTIMADEFilterConfig(FilterConfig):\n \"\"\"OPTIMADE-specific filter strategy config.\n\n Note:\n The `condition` parameter is not taken into account.\n\n \"\"\"\n\n filterType: Literal[\"optimade\", \"OPTIMADE\", \"OPTiMaDe\"] = Field(\n ...,\n description=\"The registered strategy name for OPTIMADEFilterStrategy.\",\n )\n query: Optional[str] = Field(\n None,\n description=(\n \"The `filter` OPTIMADE query parameter value. This parameter value can \"\n \"also be provided through the [`configuration.query_parameters.filter`]\"\n \"[oteapi_optimade.models.query.OPTIMADEQueryParameters.filter] parameter. \"\n \"Note, this value takes precedence over [`configuration`][oteapi_optimade.\"\n \"models.strategies.filter.OPTIMADEFilterConfig.configuration] values.\"\n ),\n )\n limit: Optional[int] = Field(\n None,\n description=(\n \"The `page_limit` OPTIMADE query parameter value. This parameter value can\"\n \" also be provided through the [`configuration.query_parameters.\"\n \"page_limit`][oteapi_optimade.models.query.OPTIMADEQueryParameters.\"\n \"page_limit] parameter. Note, this value takes precedence over \"\n \"[`configuration`][oteapi_optimade.models.strategies.filter.\"\n \"OPTIMADEFilterConfig.configuration] values.\"\n ),\n )\n configuration: OPTIMADEConfig = Field(\n OPTIMADEConfig(),\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n
OPTIMADEFilterSession (SessionUpdate)
pydantic-model
","text":"OPTIMADE session for the filter strategy.
Source code inoteapi_optimade/models/strategies/filter.py
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession.optimade_config","title":"class OPTIMADEFilterSession(SessionUpdate):\n \"\"\"OPTIMADE session for the filter strategy.\"\"\"\n\n optimade_config: Optional[OPTIMADEConfig] = Field(\n None,\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n optimade_response_object: Optional[Response] = Field(\n None,\n description=\"An OPTIMADE Python tools (OPT) pydantic response object.\",\n )\n optimade_response: Optional[Dict[str, Any]] = Field(\n None,\n description=\"An OPTIMADE response as a Python dictionary.\",\n )\n\n class Config:\n \"\"\"Pydantic configuration for `OPTIMADEFilterSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
optimade_config: OPTIMADEConfig
pydantic-field
","text":"OPTIMADE configuration. Contains relevant information necessary to perform OPTIMADE queries.
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession.optimade_response","title":"optimade_response: Dict[str, Any]
pydantic-field
","text":"An OPTIMADE response as a Python dictionary.
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession.optimade_response_object","title":"optimade_response_object: Response
pydantic-field
","text":"An OPTIMADE Python tools (OPT) pydantic response object.
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession.Config","title":"Config
","text":"Pydantic configuration for
Source code inOPTIMADEFilterSession
.oteapi_optimade/models/strategies/filter.py
"},{"location":"api_reference/models/strategies/parse/","title":"parse","text":"class Config:\n \"\"\"Pydantic configuration for `OPTIMADEFilterSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
Models specific to the parse strategy.
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEDLiteParseConfig","title":"OPTIMADEDLiteParseConfig (OPTIMADEParseConfig)
pydantic-model
","text":"OPTIMADE-specific parse strategy config.
Source code inoteapi_optimade/models/strategies/parse.py
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseConfig","title":"class OPTIMADEDLiteParseConfig(OPTIMADEParseConfig):\n \"\"\"OPTIMADE-specific parse strategy config.\"\"\"\n\n mediaType: Literal[\n \"application/vnd.optimade+dlite\",\n \"application/vnd.OPTIMADE+dlite\",\n \"application/vnd.OPTiMaDe+dlite\",\n \"application/vnd.optimade+DLite\",\n \"application/vnd.OPTIMADE+DLite\",\n \"application/vnd.OPTiMaDe+DLite\",\n ] = Field( # type: ignore[assignment]\n ...,\n description=\"The registered strategy name for OPTIMADEDLiteParseStrategy.\",\n )\n
OPTIMADEParseConfig (ResourceConfig)
pydantic-model
","text":"OPTIMADE-specific parse strategy config.
Source code inoteapi_optimade/models/strategies/parse.py
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession","title":"class OPTIMADEParseConfig(ResourceConfig):\n \"\"\"OPTIMADE-specific parse strategy config.\"\"\"\n\n downloadUrl: OPTIMADEUrl = Field(\n ...,\n description=\"Either a base OPTIMADE URL or a full OPTIMADE URL.\",\n )\n mediaType: Literal[\n \"application/vnd.optimade+json\",\n \"application/vnd.OPTIMADE+json\",\n \"application/vnd.OPTiMaDe+json\",\n \"application/vnd.optimade+JSON\",\n \"application/vnd.OPTIMADE+JSON\",\n \"application/vnd.OPTiMaDe+JSON\",\n \"application/vnd.optimade\",\n \"application/vnd.OPTIMADE\",\n \"application/vnd.OPTiMaDe\",\n ] = Field(\n ...,\n description=\"The registered strategy name for OPTIMADEParseStrategy.\",\n )\n configuration: OPTIMADEConfig = Field(\n OPTIMADEConfig(),\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n
OPTIMADEParseSession (SessionUpdate)
pydantic-model
","text":"OPTIMADE session for the parse strategy.
Source code inoteapi_optimade/models/strategies/parse.py
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession.optimade_config","title":"class OPTIMADEParseSession(SessionUpdate):\n \"\"\"OPTIMADE session for the parse strategy.\"\"\"\n\n optimade_config: Optional[OPTIMADEConfig] = Field(\n None,\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n optimade_response_object: Optional[Response] = Field(\n None,\n description=\"An OPTIMADE Python tools (OPT) pydantic response object.\",\n )\n optimade_response: Optional[Dict[str, Any]] = Field(\n None,\n description=\"An OPTIMADE response as a Python dictionary.\",\n )\n\n class Config:\n \"\"\"Pydantic configuration for `OPTIMADEParseSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
optimade_config: OPTIMADEConfig
pydantic-field
","text":"OPTIMADE configuration. Contains relevant information necessary to perform OPTIMADE queries.
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession.optimade_response","title":"optimade_response: Dict[str, Any]
pydantic-field
","text":"An OPTIMADE response as a Python dictionary.
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession.optimade_response_object","title":"optimade_response_object: Response
pydantic-field
","text":"An OPTIMADE Python tools (OPT) pydantic response object.
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession.Config","title":"Config
","text":"Pydantic configuration for
Source code inOPTIMADEParseSession
.oteapi_optimade/models/strategies/parse.py
"},{"location":"api_reference/models/strategies/resource/","title":"resource","text":"class Config:\n \"\"\"Pydantic configuration for `OPTIMADEParseSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
Models specific to the resource strategy.
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceConfig","title":"OPTIMADEResourceConfig (ResourceConfig)
pydantic-model
","text":"OPTIMADE-specific resource strategy config.
Source code inoteapi_optimade/models/strategies/resource.py
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession","title":"class OPTIMADEResourceConfig(ResourceConfig):\n \"\"\"OPTIMADE-specific resource strategy config.\"\"\"\n\n accessUrl: OPTIMADEUrl = Field(\n ...,\n description=\"Either a base OPTIMADE URL or a full OPTIMADE URL.\",\n )\n accessService: Literal[\n \"optimade\",\n \"OPTIMADE\",\n \"OPTiMaDe\",\n \"optimade+dlite\",\n \"OPTIMADE+dlite\",\n \"OPTiMaDe+dlite\",\n \"optimade+DLite\",\n \"OPTIMADE+DLite\",\n \"OPTiMaDe+DLite\",\n ] = Field(\n ...,\n description=\"The registered strategy name for OPTIMADEResourceStrategy.\",\n )\n configuration: OPTIMADEConfig = Field(\n OPTIMADEConfig(),\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n
OPTIMADEResourceSession (SessionUpdate)
pydantic-model
","text":"OPTIMADE session for the resource strategy.
Source code inoteapi_optimade/models/strategies/resource.py
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession.optimade_config","title":"class OPTIMADEResourceSession(SessionUpdate):\n \"\"\"OPTIMADE session for the resource strategy.\"\"\"\n\n optimade_config: Optional[OPTIMADEConfig] = Field(\n None,\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n optimade_resources: List[Dict[str, Any]] = Field(\n [],\n description=(\n \"List of OPTIMADE resources (structures, references, errors, ...) returned\"\n \" from the OPTIMADE request.\"\n ),\n )\n optimade_resource_model: str = Field(\n \"\",\n description=(\n \"Importable path to the resource model to be used to parse the OPTIMADE \"\n \"resources in `optimade_resource`. The importable path should be a fully \"\n \"importable path to a module separated by a colon (`:`) to then define the \"\n \"resource model class name. This means one can then do:\\n\\n```python\\n\"\n \"from PACKAGE.MODULE import RESOURCE_CLS\\n```\\nFrom the value \"\n \"`PACKAGE.MODULE:RESOURCE_CLS`\"\n ),\n regex=(\n r\"^([a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)*\" # package.module\n r\":[a-zA-Z][a-zA-Z0-9_]*)?$\" # class\n ),\n )\n\n class Config:\n \"\"\"Pydantic configuration for `OPTIMADEResourceSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
optimade_config: OPTIMADEConfig
pydantic-field
","text":"OPTIMADE configuration. Contains relevant information necessary to perform OPTIMADE queries.
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession.optimade_resource_model","title":"optimade_resource_model: ConstrainedStrValue
pydantic-field
","text":"Importable path to the resource model to be used to parse the OPTIMADE resources in
optimade_resource
. The importable path should be a fully importable path to a module separated by a colon (:
) to then define the resource model class name. This means one can then do:From the valuefrom PACKAGE.MODULE import RESOURCE_CLS\n
PACKAGE.MODULE:RESOURCE_CLS
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession.optimade_resources","title":"optimade_resources: List[Dict[str, Any]]
pydantic-field
","text":"List of OPTIMADE resources (structures, references, errors, ...) returned from the OPTIMADE request.
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession.Config","title":"Config
","text":"Pydantic configuration for
Source code inOPTIMADEResourceSession
.oteapi_optimade/models/strategies/resource.py
"},{"location":"api_reference/strategies/filter/","title":"filter","text":"class Config:\n \"\"\"Pydantic configuration for `OPTIMADEResourceSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
Demo filter strategy.
"},{"location":"api_reference/strategies/filter/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy","title":"OPTIMADEFilterStrategy
dataclass
","text":"Filter Strategy.
Implements strategies:
Source code in
(\"filterType\", \"OPTIMADE\")
(\"filterType\", \"optimade\")
(\"filterType\", \"OPTiMaDe\")
oteapi_optimade/strategies/filter.py
"},{"location":"api_reference/strategies/filter/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy.get","title":"@dataclass\nclass OPTIMADEFilterStrategy:\n \"\"\"Filter Strategy.\n\n **Implements strategies**:\n\n - `(\"filterType\", \"OPTIMADE\")`\n - `(\"filterType\", \"optimade\")`\n - `(\"filterType\", \"OPTiMaDe\")`\n\n \"\"\"\n\n filter_config: OPTIMADEFilterConfig\n\n def initialize(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEFilterSession:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Configuration values, specifically URL query parameters, can be provided to the\n OPTIMADE resource strategy through this filter strategy.\n\n Workflow:\n\n 1. Compile received information.\n 2. Update session with compiled information.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEFilterSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEFilterSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEFilterSession()\n\n if session.optimade_config:\n self.filter_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_config = self.filter_config.configuration.copy()\n\n if not optimade_config.query_parameters:\n optimade_config.query_parameters = OPTIMADEQueryParameters()\n\n if self.filter_config.query:\n LOGGER.debug(\"Setting filter from query.\")\n optimade_config.query_parameters.filter = self.filter_config.query\n\n if self.filter_config.limit:\n LOGGER.debug(\"Setting page_limit from limit.\")\n optimade_config.query_parameters.page_limit = self.filter_config.limit\n\n return session.copy(\n update={\n \"optimade_config\": optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n },\n )\n\n def get(\n self,\n session: \"Optional[Dict[str, Any]]\" = None,\n ) -> SessionUpdate:\n \"\"\"Execute the strategy.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n return SessionUpdate()\n
get(self, session=None)
","text":"Execute the strategy.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/filter.py
"},{"location":"api_reference/strategies/filter/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy.initialize","title":"def get(\n self,\n session: \"Optional[Dict[str, Any]]\" = None,\n) -> SessionUpdate:\n \"\"\"Execute the strategy.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n return SessionUpdate()\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Configuration values, specifically URL query parameters, can be provided to the OPTIMADE resource strategy through this filter strategy.
Workflow:
- Compile received information.
- Update session with compiled information.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary context.
None
Returns:
Type DescriptionOPTIMADEFilterSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/filter.py
"},{"location":"api_reference/strategies/parse/","title":"parse","text":"def initialize(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEFilterSession:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Configuration values, specifically URL query parameters, can be provided to the\n OPTIMADE resource strategy through this filter strategy.\n\n Workflow:\n\n 1. Compile received information.\n 2. Update session with compiled information.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEFilterSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEFilterSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEFilterSession()\n\n if session.optimade_config:\n self.filter_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_config = self.filter_config.configuration.copy()\n\n if not optimade_config.query_parameters:\n optimade_config.query_parameters = OPTIMADEQueryParameters()\n\n if self.filter_config.query:\n LOGGER.debug(\"Setting filter from query.\")\n optimade_config.query_parameters.filter = self.filter_config.query\n\n if self.filter_config.limit:\n LOGGER.debug(\"Setting page_limit from limit.\")\n optimade_config.query_parameters.page_limit = self.filter_config.limit\n\n return session.copy(\n update={\n \"optimade_config\": optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n },\n )\n
Demo strategy class for text/json.
"},{"location":"api_reference/strategies/parse/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy","title":"OPTIMADEParseStrategy
dataclass
","text":"Parse strategy for JSON.
Implements strategies:
Source code in
(\"mediaType\", \"application/vnd.optimade+json\")
(\"mediaType\", \"application/vnd.OPTIMADE+json\")
(\"mediaType\", \"application/vnd.OPTiMaDe+json\")
(\"mediaType\", \"application/vnd.optimade+JSON\")
(\"mediaType\", \"application/vnd.OPTIMADE+JSON\")
(\"mediaType\", \"application/vnd.OPTiMaDe+JSON\")
(\"mediaType\", \"application/vnd.optimade\")
(\"mediaType\", \"application/vnd.OPTIMADE\")
(\"mediaType\", \"application/vnd.OPTiMaDe\")
oteapi_optimade/strategies/parse.py
"},{"location":"api_reference/strategies/parse/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy.get","title":"@dataclass\nclass OPTIMADEParseStrategy:\n \"\"\"Parse strategy for JSON.\n\n **Implements strategies**:\n\n - `(\"mediaType\", \"application/vnd.optimade+json\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+json\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+json\")`\n - `(\"mediaType\", \"application/vnd.optimade+JSON\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+JSON\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+JSON\")`\n - `(\"mediaType\", \"application/vnd.optimade\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe\")`\n\n \"\"\"\n\n parse_config: OPTIMADEParseConfig\n\n def initialize(self, session: \"Optional[Dict[str, Any]]\" = None) -> SessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return SessionUpdate()\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEParseSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEParseSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEParseSession()\n\n if session.optimade_config:\n self.parse_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n cache = DataCache(self.parse_config.configuration.datacache_config)\n if self.parse_config.downloadUrl in cache:\n response: \"Dict[str, Any]\" = cache.get(self.parse_config.downloadUrl)\n elif (\n self.parse_config.configuration.datacache_config.accessKey\n and self.parse_config.configuration.datacache_config.accessKey in cache\n ):\n response = cache.get(\n self.parse_config.configuration.datacache_config.accessKey\n )\n else:\n download_config = self.parse_config.copy()\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n response = {\"json\": json.loads(cache.get(session.pop(\"key\")))}\n\n if (\n not response.get(\"ok\", True)\n or (\n 200 > response.get(\"status_code\", 200)\n or response.get(\"status_code\", 200) >= 300\n )\n or \"errors\" in response.get(\"json\", {})\n ):\n # Error response\n try:\n response_object = ErrorResponse(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Could not validate an error response.\\nValidationError: \"\n \"%s\\nresponse=%r\",\n exc,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate an error response.\"\n ) from exc\n else:\n # Successful response\n response_model = self.parse_config.downloadUrl.response_model()\n if response_model:\n if not isinstance(response_model, tuple):\n response_model = (response_model,)\n for model_cls in response_model:\n try:\n response_object = model_cls(**response.get(\"json\", {}))\n except ValidationError:\n pass\n else:\n break\n else:\n LOGGER.error(\n \"Could not validate for an expected response model.\\nURL=%r\\n\"\n \"response_models=%r\\nresponse=%s\",\n self.parse_config.downloadUrl,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate for an expected response model.\"\n )\n else:\n # No \"endpoint\" or unknown\n try:\n response_object = Success(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Unknown or unparseable endpoint.\\nValidatonError: %s\\n\"\n \"URL=%r\\nendpoint=%r\\nresponse_model=%r\\nresponse=%s\",\n exc,\n self.parse_config.downloadUrl,\n self.parse_config.downloadUrl.endpoint,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Unknown or unparseable endpoint.\"\n ) from exc\n\n if self.parse_config.configuration.return_object:\n session.optimade_response_object = response_object\n else:\n session.optimade_response = model2dict(response_object)\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
get(self, session=None)
","text":"Request and parse an OPTIMADE response using OPT.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromdownloadUrl
.Workflow:
- Request OPTIMADE response.
- Parse as an OPTIMADE Python tools (OPT) pydantic response model.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEParseSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/parse.py
"},{"location":"api_reference/strategies/parse/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEParseSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEParseSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEParseSession()\n\n if session.optimade_config:\n self.parse_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n cache = DataCache(self.parse_config.configuration.datacache_config)\n if self.parse_config.downloadUrl in cache:\n response: \"Dict[str, Any]\" = cache.get(self.parse_config.downloadUrl)\n elif (\n self.parse_config.configuration.datacache_config.accessKey\n and self.parse_config.configuration.datacache_config.accessKey in cache\n ):\n response = cache.get(\n self.parse_config.configuration.datacache_config.accessKey\n )\n else:\n download_config = self.parse_config.copy()\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n response = {\"json\": json.loads(cache.get(session.pop(\"key\")))}\n\n if (\n not response.get(\"ok\", True)\n or (\n 200 > response.get(\"status_code\", 200)\n or response.get(\"status_code\", 200) >= 300\n )\n or \"errors\" in response.get(\"json\", {})\n ):\n # Error response\n try:\n response_object = ErrorResponse(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Could not validate an error response.\\nValidationError: \"\n \"%s\\nresponse=%r\",\n exc,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate an error response.\"\n ) from exc\n else:\n # Successful response\n response_model = self.parse_config.downloadUrl.response_model()\n if response_model:\n if not isinstance(response_model, tuple):\n response_model = (response_model,)\n for model_cls in response_model:\n try:\n response_object = model_cls(**response.get(\"json\", {}))\n except ValidationError:\n pass\n else:\n break\n else:\n LOGGER.error(\n \"Could not validate for an expected response model.\\nURL=%r\\n\"\n \"response_models=%r\\nresponse=%s\",\n self.parse_config.downloadUrl,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate for an expected response model.\"\n )\n else:\n # No \"endpoint\" or unknown\n try:\n response_object = Success(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Unknown or unparseable endpoint.\\nValidatonError: %s\\n\"\n \"URL=%r\\nendpoint=%r\\nresponse_model=%r\\nresponse=%s\",\n exc,\n self.parse_config.downloadUrl,\n self.parse_config.downloadUrl.endpoint,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Unknown or unparseable endpoint.\"\n ) from exc\n\n if self.parse_config.configuration.return_object:\n session.optimade_response_object = response_object\n else:\n session.optimade_response = model2dict(response_object)\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/parse.py
"},{"location":"api_reference/strategies/resource/","title":"resource","text":"def initialize(self, session: \"Optional[Dict[str, Any]]\" = None) -> SessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return SessionUpdate()\n
OPTIMADE resource strategy.
"},{"location":"api_reference/strategies/resource/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy","title":"OPTIMADEResourceStrategy
dataclass
","text":"OPTIMADE Resource Strategy.
Implements strategies:
Source code in
(\"accessService\", \"optimade\")
(\"accessService\", \"OPTIMADE\")
(\"accessService\", \"OPTiMaDe\")
(\"accessService\", \"optimade+dlite\")
(\"accessService\", \"OPTIMADE+dlite\")
(\"accessService\", \"OPTiMaDe+dlite\")
(\"accessService\", \"optimade+DLite\")
(\"accessService\", \"OPTIMADE+DLite\")
(\"accessService\", \"OPTiMaDe+DLite\")
oteapi_optimade/strategies/resource.py
"},{"location":"api_reference/strategies/resource/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy.get","title":"@dataclass\nclass OPTIMADEResourceStrategy:\n \"\"\"OPTIMADE Resource Strategy.\n\n **Implements strategies**:\n\n - `(\"accessService\", \"optimade\")`\n - `(\"accessService\", \"OPTIMADE\")`\n - `(\"accessService\", \"OPTiMaDe\")`\n - `(\"accessService\", \"optimade+dlite\")`\n - `(\"accessService\", \"OPTIMADE+dlite\")`\n - `(\"accessService\", \"OPTiMaDe+dlite\")`\n - `(\"accessService\", \"optimade+DLite\")`\n - `(\"accessService\", \"OPTIMADE+DLite\")`\n - `(\"accessService\", \"OPTiMaDe+DLite\")`\n\n \"\"\"\n\n resource_config: OPTIMADEResourceConfig\n\n def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n ) -> \"Union[SessionUpdate, DLiteSessionUpdate]\":\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n ):\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n return SessionUpdate()\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEResourceSession:\n \"\"\"Execute an OPTIMADE query to `accessUrl`.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `accessUrl`.\n\n Workflow:\n 1. Update configuration according to session.\n 2. Deconstruct `accessUrl` (done partly by\n `oteapi_optimade.models.custom_types.OPTIMADEUrl`).\n 3. Reconstruct the complete query URL.\n 4. Send query.\n 5. Store result in data cache.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEResourceSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEResourceSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEResourceSession()\n\n if session.optimade_config:\n self.resource_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_endpoint = self.resource_config.accessUrl.endpoint or \"structures\"\n optimade_query = (\n self.resource_config.configuration.query_parameters\n or OPTIMADEQueryParameters()\n )\n LOGGER.debug(\"resource_config: %r\", self.resource_config)\n\n if self.resource_config.accessUrl.query:\n parsed_query = parse_qs(self.resource_config.accessUrl.query)\n for field, value in parsed_query.items():\n # Only use the latest defined value for any parameter\n if field not in optimade_query.__fields_set__:\n LOGGER.debug(\n \"Setting %r from accessUrl (value=%r)\", field, value[-1]\n )\n setattr(optimade_query, field, value[-1])\n\n LOGGER.debug(\"optimade_query after update: %r\", optimade_query)\n\n optimade_url = OPTIMADEUrl(\n f\"{self.resource_config.accessUrl.base_url}\"\n f\"/{self.resource_config.accessUrl.version or 'v1'}\"\n f\"/{optimade_endpoint}?{optimade_query.generate_query_string()}\"\n )\n LOGGER.debug(\"OPTIMADE URL to be requested: %s\", optimade_url)\n\n # Set cache access key to the full OPTIMADE URL.\n self.resource_config.configuration.datacache_config.accessKey = optimade_url\n\n # Perform query\n response = requests.get(\n optimade_url,\n allow_redirects=True,\n timeout=(3, 27), # timeout in seconds (connect, read)\n )\n\n if optimade_query.response_format and optimade_query.response_format != \"json\":\n raise NotImplementedError(\n \"Can only handle JSON responses for now. Requested response format: \"\n f\"{optimade_query.response_format!r}\"\n )\n\n cache = DataCache(config=self.resource_config.configuration.datacache_config)\n cache.add(\n {\n \"status_code\": response.status_code,\n \"ok\": response.ok,\n \"json\": response.json(),\n }\n )\n\n parse_with_dlite = use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n )\n\n parse_mediaType = (\n \"application/vnd.\"\n f\"{self.resource_config.accessService.split('+', maxsplit=1)[0]}\"\n )\n if parse_with_dlite:\n parse_mediaType += \"+DLite\"\n elif optimade_query.response_format:\n parse_mediaType += f\"+{optimade_query.response_format}\"\n\n parse_config = {\n \"downloadUrl\": optimade_url,\n \"mediaType\": parse_mediaType,\n \"configuration\": {\n \"datacache_config\": self.resource_config.configuration.datacache_config,\n \"return_object\": True,\n },\n }\n\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the session.\"\n )\n optimade_response: \"OPTIMADEResponse\" = session.pop(\"optimade_response_object\")\n if \"optimade_response\" in session and not session.get(\"optimade_response\"):\n del session[\"optimade_response\"]\n\n if isinstance(optimade_response, ErrorResponse):\n optimade_resources = optimade_response.errors\n session.optimade_resource_model = (\n f\"{OptimadeError.__module__}:OptimadeError\"\n )\n elif isinstance(optimade_response, ReferenceResponseMany):\n optimade_resources = [\n Reference(entry).as_dict\n if isinstance(entry, dict)\n else Reference(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, ReferenceResponseOne):\n optimade_resources = [\n Reference(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Reference(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, StructureResponseMany):\n optimade_resources = [\n Structure(entry).as_dict\n if isinstance(entry, dict)\n else Structure(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n elif isinstance(optimade_response, StructureResponseOne):\n optimade_resources = [\n Structure(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Structure(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n else:\n LOGGER.debug(\n \"Could not parse response as errors, references or structures. \"\n \"Response:\\n%r\",\n optimade_response,\n )\n raise OPTIMADEParseError(\n \"Could not retrieve errors, references or structures from response \"\n f\"from {optimade_url}. It could be a valid OPTIMADE API response, \"\n \"however it may not be supported by OTEAPI-OPTIMADE. It may also be an \"\n \"invalid response completely.\"\n )\n\n session.optimade_resources = [\n model2dict(resource) for resource in optimade_resources\n ]\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
get(self, session=None)
","text":"Execute an OPTIMADE query to
accessUrl
.This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromaccessUrl
.Workflow: 1. Update configuration according to session. 2. Deconstruct
accessUrl
(done partly byoteapi_optimade.models.custom_types.OPTIMADEUrl
). 3. Reconstruct the complete query URL. 4. Send query. 5. Store result in data cache.Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEResourceSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"api_reference/strategies/resource/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEResourceSession:\n \"\"\"Execute an OPTIMADE query to `accessUrl`.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `accessUrl`.\n\n Workflow:\n 1. Update configuration according to session.\n 2. Deconstruct `accessUrl` (done partly by\n `oteapi_optimade.models.custom_types.OPTIMADEUrl`).\n 3. Reconstruct the complete query URL.\n 4. Send query.\n 5. Store result in data cache.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEResourceSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEResourceSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEResourceSession()\n\n if session.optimade_config:\n self.resource_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_endpoint = self.resource_config.accessUrl.endpoint or \"structures\"\n optimade_query = (\n self.resource_config.configuration.query_parameters\n or OPTIMADEQueryParameters()\n )\n LOGGER.debug(\"resource_config: %r\", self.resource_config)\n\n if self.resource_config.accessUrl.query:\n parsed_query = parse_qs(self.resource_config.accessUrl.query)\n for field, value in parsed_query.items():\n # Only use the latest defined value for any parameter\n if field not in optimade_query.__fields_set__:\n LOGGER.debug(\n \"Setting %r from accessUrl (value=%r)\", field, value[-1]\n )\n setattr(optimade_query, field, value[-1])\n\n LOGGER.debug(\"optimade_query after update: %r\", optimade_query)\n\n optimade_url = OPTIMADEUrl(\n f\"{self.resource_config.accessUrl.base_url}\"\n f\"/{self.resource_config.accessUrl.version or 'v1'}\"\n f\"/{optimade_endpoint}?{optimade_query.generate_query_string()}\"\n )\n LOGGER.debug(\"OPTIMADE URL to be requested: %s\", optimade_url)\n\n # Set cache access key to the full OPTIMADE URL.\n self.resource_config.configuration.datacache_config.accessKey = optimade_url\n\n # Perform query\n response = requests.get(\n optimade_url,\n allow_redirects=True,\n timeout=(3, 27), # timeout in seconds (connect, read)\n )\n\n if optimade_query.response_format and optimade_query.response_format != \"json\":\n raise NotImplementedError(\n \"Can only handle JSON responses for now. Requested response format: \"\n f\"{optimade_query.response_format!r}\"\n )\n\n cache = DataCache(config=self.resource_config.configuration.datacache_config)\n cache.add(\n {\n \"status_code\": response.status_code,\n \"ok\": response.ok,\n \"json\": response.json(),\n }\n )\n\n parse_with_dlite = use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n )\n\n parse_mediaType = (\n \"application/vnd.\"\n f\"{self.resource_config.accessService.split('+', maxsplit=1)[0]}\"\n )\n if parse_with_dlite:\n parse_mediaType += \"+DLite\"\n elif optimade_query.response_format:\n parse_mediaType += f\"+{optimade_query.response_format}\"\n\n parse_config = {\n \"downloadUrl\": optimade_url,\n \"mediaType\": parse_mediaType,\n \"configuration\": {\n \"datacache_config\": self.resource_config.configuration.datacache_config,\n \"return_object\": True,\n },\n }\n\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the session.\"\n )\n optimade_response: \"OPTIMADEResponse\" = session.pop(\"optimade_response_object\")\n if \"optimade_response\" in session and not session.get(\"optimade_response\"):\n del session[\"optimade_response\"]\n\n if isinstance(optimade_response, ErrorResponse):\n optimade_resources = optimade_response.errors\n session.optimade_resource_model = (\n f\"{OptimadeError.__module__}:OptimadeError\"\n )\n elif isinstance(optimade_response, ReferenceResponseMany):\n optimade_resources = [\n Reference(entry).as_dict\n if isinstance(entry, dict)\n else Reference(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, ReferenceResponseOne):\n optimade_resources = [\n Reference(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Reference(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, StructureResponseMany):\n optimade_resources = [\n Structure(entry).as_dict\n if isinstance(entry, dict)\n else Structure(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n elif isinstance(optimade_response, StructureResponseOne):\n optimade_resources = [\n Structure(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Structure(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n else:\n LOGGER.debug(\n \"Could not parse response as errors, references or structures. \"\n \"Response:\\n%r\",\n optimade_response,\n )\n raise OPTIMADEParseError(\n \"Could not retrieve errors, references or structures from response \"\n f\"from {optimade_url}. It could be a valid OPTIMADE API response, \"\n \"however it may not be supported by OTEAPI-OPTIMADE. It may also be an \"\n \"invalid response completely.\"\n )\n\n session.optimade_resources = [\n model2dict(resource) for resource in optimade_resources\n ]\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionUnion[SessionUpdate, DLiteSessionUpdate]
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"api_reference/strategies/resource/#oteapi_optimade.strategies.resource.use_dlite","title":"def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n) -> \"Union[SessionUpdate, DLiteSessionUpdate]\":\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n ):\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n return SessionUpdate()\n
use_dlite(access_service, use_dlite_flag)
","text":"Determine whether DLite should be utilized in the Resource strategy.
Parameters:
Name Type Description Defaultaccess_service
str
The accessService value from the resource's configuration.
requireduse_dlite_flag
bool
The strategy-specific
requireduse_dlite
configuration option.Returns:
Type Descriptionbool
Based on the accessService value, then whether DLite should be used or not.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"examples/","title":"Overview","text":"def use_dlite(access_service: str, use_dlite_flag: bool) -> bool:\n \"\"\"Determine whether DLite should be utilized in the Resource strategy.\n\n Parameters:\n access_service: The accessService value from the resource's configuration.\n use_dlite_flag: The strategy-specific `use_dlite` configuration option.\n\n Returns:\n Based on the accessService value, then whether DLite should be used or not.\n\n \"\"\"\n if (\n any(dlite_form in access_service for dlite_form in [\"DLite\", \"dlite\"])\n or use_dlite_flag\n ):\n if oteapi_dlite_version is None:\n raise MissingDependency(\n \"OTEAPI-DLite is not found on the system. This is required to use \"\n \"DLite with the OTEAPI-OPTIMADE strategies.\"\n )\n return True\n return False\n
This section provides examples of how to use this OTEAPI plugin to perform OPTIMADE queries and handle the results.
In Use OTEAPI-OPTIMADE with OTElib you can find an example of how to use this plugin with the OTElib client.
It is worth noting that there are several different ways to use the strategies in this plugin. For example, an OPTIMADE query can be provided using the
OPTIMADE
filter strategy, but it can also be provided directly in the URL value of theOPTIMADE
data resource strategy'saccessUlr
parameter. Further, it could be set through aconfiguration
parameter entry to either of these strategies.In the examples only one of these options are given, and this is the same for other aspects: What we believe is the most common and transparent use case is given.
Finally, it is important to note that using OTElib directly is not intended for end users. Using OTElib should be done as a backend task in a web application, and the results should be presented to the end user in a more user friendly way.
"},{"location":"examples/#setup-for-examples","title":"Setup for examples","text":""},{"location":"examples/#prerequisites","title":"Prerequisites","text":"To run the examples locally, you need to have the following tools available (in addition to a working Python 3.9+ installation):
"},{"location":"examples/#jupyter-installation","title":"Jupyter installation","text":"
- Jupyter
- Docker (or similar containerization tool)
To install Jupyter, please refer to the Jupyter documentation. If you want to use
pip
to install Jupyter, you can do so by installing theexamples
extra for this plugin package:pip install oteapi-optimade[examples]\n
This will also install OTElib and any other Python packages you may need for the examples.
"},{"location":"examples/#docker-installation","title":"Docker installation","text":"To install Docker, please refer to the Docker documentation.
"},{"location":"examples/#start-a-local-oteapi-server","title":"Start a local OTEAPI server","text":"When running a local OTEAPI server, you need to ensure the OTEAPI-OPTIMADE plugin is installed. This can be done by using the
OTEAPI_PLUGIN_PACKAGES
environment variables as described in the OTEAPI Services README.There are two methods of starting the server:
- Using Docker
- Using Docker Compose
No matter the method, the server will be available at
"},{"location":"examples/#using-docker","title":"Using Docker","text":"http://localhost:80/
. To check it, go to the/docs
endpoint: localhost:80/docs.There are no extra files needed to start the server using Docker. However, several commands need to be run to start the server, which is a collection of different microservices running in different containers on the same Docker network.
The general setup is outlined in the OTEAPI Services README.
For convenience, the following commands can be used to start the services:
docker network create otenet\ndocker volume create redis-persist\ndocker run \\\n --detach \\\n --name redis \\\n --volume redis-persist:/data \\\n --network otenet \\\n redis:latest\ndocker run \\\n --rm \\\n --network otenet \\\n --detach \\\n --publish 80:8080 \\\n --env OTEAPI_REDIS_TYPE=redis \\\n --env OTEAPI_REDIS_HOST=redis \\\n --env OTEAPI_REDIS_PORT=6379 \\\n --env OTEAPI_INCLUDE_REDISADMIN=False \\\n --env OTEAPI_EXPOSE_SECRETS=True \\\n --env OTEAPI_PLUGIN_PACKAGES=oteapi-optimade \\\n ghcr.io/emmc-asbl/oteapi:latest\n
Note
To use the
"},{"location":"examples/#using-docker-compose","title":"Using Docker Compose","text":"/triples
endpoint, an AllegroGraph triplestore needs to be running. For more information see the OTEAPI Services README to see how to set this up and run it.Download the Docker Compose file from the OTEAPI Services repository:
curl -O https://raw.githubusercontent.com/EMMC-ASBL/oteapi-services/master/docker-compose.yml\n
And either update the
OTEAPI_PLUGIN_PACKAGES
environment variable in the file to includeoteapi-optimade
:# ...\n OTEAPI_PLUGIN_PACKAGES: oteapi-optimade\n# ...\n
Or set the environment variable when starting the services by prefixing it to the Docker Compose command.
Then start the services:
docker compose pull\ndocker compose up --detach\n
Note
When setting the environment variables as a prefix to the
docker compose
command, it is only needed for the command that runs the services:"},{"location":"examples/dlite/","title":"Use DLite strategies from OTEAPI-OPTIMADE","text":"In\u00a0[1]: Copied!OTEAPI_PLUGIN_PACKAGES=oteapi-optimade docker compose up --detach\n
from otelib import OTEClient\n\nclient = OTEClient(\"python\")\nfrom otelib import OTEClient client = OTEClient(\"python\") In\u00a0[2]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE+DLite\",\n accessUrl=\"https://optimade.materialsproject.org\",\n)\n\n# This is equivalent to:\n# data_resource_strategy = client.create_dataresource(\n# accessService=\"OPTIMADE\",\n# accessUrl=\"https://optimade.materialsproject.org\",\n# configuration={\"use_dlite\": True},\n# )\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE+DLite\", accessUrl=\"https://optimade.materialsproject.org\", ) # This is equivalent to: # data_resource_strategy = client.create_dataresource( # accessService=\"OPTIMADE\", # accessUrl=\"https://optimade.materialsproject.org\", # configuration={\"use_dlite\": True}, # ) In\u00a0[3]: Copied!filter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='elements HAS ALL \"Si\",\"O\" AND nelements<=4',\n)\nfilter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='elements HAS ALL \"Si\",\"O\" AND nelements<=4', ) In\u00a0[4]: Copied!import json\n\npipeline = filter_strategy >> data_resource_strategy\nsession = pipeline.get()\nparsed_session = json.loads(session)\nparsed_session.keys()\nimport json pipeline = filter_strategy >> data_resource_strategy session = pipeline.get() parsed_session = json.loads(session) parsed_session.keys()/opt/hostedtoolcache/Python/3.9.18/x64/lib/python3.9/site-packages/optimade/server/config.py:113: UserWarning: Unable to find config file at /home/runner/.optimade.json, using the default settings instead.\n warnings.warn(\nOut[4]:dict_keys(['optimade_config', 'optimade_resources', 'optimade_resource_model', 'collection_id'])In\u00a0[5]: Copied!from importlib import import_module\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\n\nparsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\")\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")\nfrom importlib import import_module import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) parsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\") print(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")The query resulted in 20 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1033911', 'mp-757887', 'mp-1219366', 'mp-733539', 'mp-542090', 'mp-758465', 'mp-560675', 'mp-774171', 'mp-1304778', 'mp-1016821', 'mp-1363556', 'mp-757013', 'mp-683953', 'mp-1020609', 'mp-17612', 'mp-558129', 'mp-1210628', 'mp-1197149', 'mp-21791', 'mp-752892']\nIn\u00a0[6]: Copied!from oteapi_dlite.utils import get_collection\n\ncollection = get_collection(session=parsed_session)\nprint(collection)\nfrom oteapi_dlite.utils import get_collection collection = get_collection(session=parsed_session) print(collection){\n \"c2469867-d0a3-4531-b13c-939706e3138b\": {\n \"meta\": \"http://onto-ns.com/meta/0.1/Collection\",\n \"dimensions\": {\n \"nrelations\": 60\n },\n \"properties\": {\n \"relations\": [[\"mp-1033911\", \"_is-a\", \"Instance\"], [\"mp-1033911\", \"_has-uuid\", \"d8e52b5c-ffac-4ce7-a6b3-5e8abbda868c\"], [\"mp-1033911\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-757887\", \"_is-a\", \"Instance\"], [\"mp-757887\", \"_has-uuid\", \"41533e1a-fd71-490e-b880-7e2f7faba0cf\"], [\"mp-757887\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1219366\", \"_is-a\", \"Instance\"], [\"mp-1219366\", \"_has-uuid\", \"d256ad04-24c2-46e9-85d9-b4a7f1216e3c\"], [\"mp-1219366\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-733539\", \"_is-a\", \"Instance\"], [\"mp-733539\", \"_has-uuid\", \"2c487685-85a5-49cf-bc65-7f9250226d8a\"], [\"mp-733539\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-542090\", \"_is-a\", \"Instance\"], [\"mp-542090\", \"_has-uuid\", \"c29ce187-2ea2-4054-a7f4-f7fe38146160\"], [\"mp-542090\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-758465\", \"_is-a\", \"Instance\"], [\"mp-758465\", \"_has-uuid\", \"86963bbc-bea6-4f9f-86d4-05d9f1009be1\"], [\"mp-758465\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-560675\", \"_is-a\", \"Instance\"], [\"mp-560675\", \"_has-uuid\", \"152f9f71-0d4a-4719-9cad-1fc07c9e32e4\"], [\"mp-560675\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-774171\", \"_is-a\", \"Instance\"], [\"mp-774171\", \"_has-uuid\", \"f02b3a60-0991-4f63-966a-47bce4ee568b\"], [\"mp-774171\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1304778\", \"_is-a\", \"Instance\"], [\"mp-1304778\", \"_has-uuid\", \"d0a7119f-c457-4547-8faf-dee63a3a28b0\"], [\"mp-1304778\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1016821\", \"_is-a\", \"Instance\"], [\"mp-1016821\", \"_has-uuid\", \"41b1d16a-dc3e-48d1-9123-cf31f4ea0556\"], [\"mp-1016821\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1363556\", \"_is-a\", \"Instance\"], [\"mp-1363556\", \"_has-uuid\", \"96eb3a9d-aa81-4ab7-92a2-526d3b270c6c\"], [\"mp-1363556\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-757013\", \"_is-a\", \"Instance\"], [\"mp-757013\", \"_has-uuid\", \"7a3772db-ea1a-4971-95e7-298f373c7b7f\"], [\"mp-757013\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-683953\", \"_is-a\", \"Instance\"], [\"mp-683953\", \"_has-uuid\", \"498f0cea-fcff-4752-ac69-0b9133475d7a\"], [\"mp-683953\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1020609\", \"_is-a\", \"Instance\"], [\"mp-1020609\", \"_has-uuid\", \"57181f0d-229a-4ac5-8840-d1205862f577\"], [\"mp-1020609\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-17612\", \"_is-a\", \"Instance\"], [\"mp-17612\", \"_has-uuid\", \"4d7ab767-25d5-4267-87e1-14355d6c1b6f\"], [\"mp-17612\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-558129\", \"_is-a\", \"Instance\"], [\"mp-558129\", \"_has-uuid\", \"713fe9b3-2ac8-46df-b9f1-285ccac296f9\"], [\"mp-558129\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1210628\", \"_is-a\", \"Instance\"], [\"mp-1210628\", \"_has-uuid\", \"1da03470-55d7-48bd-89a5-a41fe271e63a\"], [\"mp-1210628\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1197149\", \"_is-a\", \"Instance\"], [\"mp-1197149\", \"_has-uuid\", \"4ac79d7e-c033-4f7a-9fad-846c6a1f1d41\"], [\"mp-1197149\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-21791\", \"_is-a\", \"Instance\"], [\"mp-21791\", \"_has-uuid\", \"a67456d3-f3be-4500-b860-cff4706bce00\"], [\"mp-21791\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-752892\", \"_is-a\", \"Instance\"], [\"mp-752892\", \"_has-uuid\", \"12441aec-e99f-4e80-b923-9fd8ad0a641b\"], [\"mp-752892\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"]]\n }\n }\n}\nIn\u00a0[7]: Copied!for inst in collection.get_instances():\n print(inst)\nfor inst in collection.get_instances(): print(inst){\n \"d8e52b5c-ffac-4ce7-a6b3-5e8abbda868c\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"3990d434-82ae-4451-a97f-2e66e9ac3aab\",\n \"id\": \"mp-1033911\"\n }\n }\n}\n{\n \"41533e1a-fd71-490e-b880-7e2f7faba0cf\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"cdfb1f36-eea7-49c8-901f-63c1784d30f9\",\n \"id\": \"mp-757887\"\n }\n }\n}\n{\n \"d256ad04-24c2-46e9-85d9-b4a7f1216e3c\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"704c7790-ce6d-48ec-9fe8-f9498255a494\",\n \"id\": \"mp-1219366\"\n }\n }\n}\n{\n \"2c487685-85a5-49cf-bc65-7f9250226d8a\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"cfe3d459-1961-408c-b039-d13378366f13\",\n \"id\": \"mp-733539\"\n }\n }\n}\n{\n \"c29ce187-2ea2-4054-a7f4-f7fe38146160\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"08c02f72-682b-4798-8010-c4c693778c9d\",\n \"id\": \"mp-542090\"\n }\n }\n}\n{\n \"86963bbc-bea6-4f9f-86d4-05d9f1009be1\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"53e7e2d7-88db-482b-ab0f-4ff680507718\",\n \"id\": \"mp-758465\"\n }\n }\n}\n{\n \"152f9f71-0d4a-4719-9cad-1fc07c9e32e4\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"124d794e-a280-41f5-850d-16d91f455fb8\",\n \"id\": \"mp-560675\"\n }\n }\n}\n{\n \"f02b3a60-0991-4f63-966a-47bce4ee568b\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"297c07ab-d5fb-4354-862d-46447490502b\",\n \"id\": \"mp-774171\"\n }\n }\n}\n{\n \"d0a7119f-c457-4547-8faf-dee63a3a28b0\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"81cb7e0b-e9f4-42f7-875d-633040c23326\",\n \"id\": \"mp-1304778\"\n }\n }\n}\n{\n \"41b1d16a-dc3e-48d1-9123-cf31f4ea0556\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"3456a598-3c51-403f-80c2-fe3b91ce1c5b\",\n \"id\": \"mp-1016821\"\n }\n }\n}\n{\n \"96eb3a9d-aa81-4ab7-92a2-526d3b270c6c\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"c20e7851-c8bf-458e-91cd-d7ae229b280b\",\n \"id\": \"mp-1363556\"\n }\n }\n}\n{\n \"7a3772db-ea1a-4971-95e7-298f373c7b7f\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"3a5a032b-ba28-4782-8fd5-6f3751a817c8\",\n \"id\": \"mp-757013\"\n }\n }\n}\n{\n \"498f0cea-fcff-4752-ac69-0b9133475d7a\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"ad1863d8-7edc-4957-b0ef-250f11fe1f96\",\n \"id\": \"mp-683953\"\n }\n }\n}\n{\n \"57181f0d-229a-4ac5-8840-d1205862f577\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"b1dbd5f6-c5cd-4ff2-8cb6-e1e904a04957\",\n \"id\": \"mp-1020609\"\n }\n }\n}\n{\n \"4d7ab767-25d5-4267-87e1-14355d6c1b6f\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"60c8b965-de2c-4019-a2f5-635d6cca82bd\",\n \"id\": \"mp-17612\"\n }\n }\n}\n{\n \"713fe9b3-2ac8-46df-b9f1-285ccac296f9\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"34c17beb-8f1e-4da6-b7c2-d3e98b3ef71e\",\n \"id\": \"mp-558129\"\n }\n }\n}\n{\n \"1da03470-55d7-48bd-89a5-a41fe271e63a\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"e99bdb75-0fba-4bbc-9ae8-ff1f01f6879f\",\n \"id\": \"mp-1210628\"\n }\n }\n}\n{\n \"4ac79d7e-c033-4f7a-9fad-846c6a1f1d41\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"77cf62ea-8a77-4d50-a123-d8db5b400fe2\",\n \"id\": \"mp-1197149\"\n }\n }\n}\n{\n \"a67456d3-f3be-4500-b860-cff4706bce00\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"bac7a055-d075-406a-8da3-16ced4010d44\",\n \"id\": \"mp-21791\"\n }\n }\n}\n{\n \"12441aec-e99f-4e80-b923-9fd8ad0a641b\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"22549275-83e7-4fb6-a05f-6b29e512cbef\",\n \"id\": \"mp-752892\"\n }\n }\n}\nIn\u00a0[8]: Copied!import dlite\n\nstructure_instances: list[dlite.Instance] = list(collection.get_instances())\n\nstructure_attributes_instance: dlite.Instance = dlite.get_instance(structure_instances[0].attributes)\n\nprint(structure_attributes_instance)\nimport dlite structure_instances: list[dlite.Instance] = list(collection.get_instances()) structure_attributes_instance: dlite.Instance = dlite.get_instance(structure_instances[0].attributes) print(structure_attributes_instance){\n \"3990d434-82ae-4451-a97f-2e66e9ac3aab\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructureAttributes\",\n \"dimensions\": {\n \"nelements\": 4,\n \"dimensionality\": 3,\n \"nsites\": 32,\n \"nspecies\": 4,\n \"nstructure_features\": 0\n },\n \"properties\": {\n \"elements\": [\"K\", \"Mg\", \"O\", \"Si\"],\n \"elements_ratios\": [0.03125, 0.4375, 0.03125, 0.5],\n \"chemical_formula_descriptive\": \"KMg14O16Si\",\n \"chemical_formula_reduced\": \"KMg14O16Si\",\n \"chemical_formula_hill\": \"KMg14O16Si\",\n \"chemical_formula_anonymous\": \"A16B14CD\",\n \"dimension_types\": [1, 1, 1],\n \"nperiodic_dimensions\": 3,\n \"lattice_vectors\": [[8.59318, 0, 0], [0, 8.59318, 0], [0, 0, 4.41201]],\n \"cartesian_site_positions\": [[0, 0, 0], [0, 4.29659, 0], [4.29659, 0, 0], [0, 2.1438, 2.20601], [0, 6.44938, 2.20601], [4.29659, 2.13079, 2.20601], [4.29659, 6.46238, 2.20601], [2.1438, 0, 2.20601], [2.13079, 4.29659, 2.20601], [6.44938, 0, 2.20601], [6.46238, 4.29659, 2.20601], [2.12673, 2.12673, 0], [2.12673, 6.46645, 0], [6.46645, 2.12673, 0], [6.46645, 6.46645, 0], [4.29659, 4.29659, 0], [2.37689, 0, 0], [2.32997, 4.29659, 0], [6.21628, 0, 0], [6.2632, 4.29659, 0], [2.16162, 2.16162, 2.20601], [2.16162, 6.43155, 2.20601], [6.43155, 2.16162, 2.20601], [6.43155, 6.43155, 2.20601], [0, 0, 2.20601], [0, 4.29659, 2.20601], [4.29659, 0, 2.20601], [4.29659, 4.29659, 2.20601], [0, 2.37689, 0], [0, 6.21628, 0], [4.29659, 2.32997, 0], [4.29659, 6.2632, 0]],\n \"species\": [\"86fdf62d-a887-4314-a168-b925ef1585da\", \"61325ec0-c046-43c9-89af-89b9903d9a80\", \"14f54cf4-e9e1-4c0a-8449-fedf91920163\", \"afd06f26-0e2d-481f-9a55-3cac841dd66f\"],\n \"species_at_sites\": [\"K\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Si\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\"],\n \"assemblies\": null,\n \"structure_features\": [],\n \"immutable_id\": \"645d2b74bcd30f748b4746a3\",\n \"last_modified\": \"2019-10-23 12:27:13+00:00\"\n }\n }\n}\n"},{"location":"examples/dlite/#use-dlite-strategies-from-oteapi-optimade","title":"Use DLite strategies from OTEAPI-OPTIMADE\u00b6","text":"This example shows how to use the DLite strategies from the OTEAPI-OPTIMADE plugin.
DLite is a Python library for working with data models and semantics. It is considered to be the default semantic data model backend for use with OTEAPI.
To see more fundamental examples of how to use OTEAPI-OPTIMADE, see the example Use OTEAPI-OPTIMADE with OTElib. OTElib will also be used as a client in the current example. Furthermore, only the HTTP requests-based backend will be used in this example.
"},{"location":"examples/dlite/#setup","title":"Setup\u00b6","text":"Please see the setup instructions in Use OTEAPI-OPTIMADE with OTElib for how to ensure you have a proper environment to run the example in.
"},{"location":"examples/dlite/#example","title":"Example\u00b6","text":"In this example, we will use the DLite strategies to query the Materials Project OPTIMADE API. We are interested in finding all structures that include the elements
"},{"location":"examples/dlite/#create-a-client","title":"Create a client\u00b6","text":"Si
andO
, and that have a maximum of 4 elements in total.Let's start by initializing a client:
"},{"location":"examples/dlite/#data-resource-strategy","title":"Data Resource strategy\u00b6","text":"The general pipeline is the same as it was for Use OTEAPI-OPTIMADE with OTElib:
However, in order to use DLite we need to either reference a specific
accessService
or set a flag in the configuration of the data resource strategy.Note, it is only for the data resource strategy we need to specify the usage of DLite, as the filter strategy is generic and merely a helper for more explicitly setting the query parameters to be used for the underlying OPTIMADE query.
"},{"location":"examples/dlite/#filter-strategy","title":"Filter strategy\u00b6","text":"The OPTIMADE filter query to fulfill the interests outlined above should look like this:
"},{"location":"examples/dlite/#setup-execute-and-inspect-the-pipeline","title":"Setup, execute and inspect the pipeline\u00b6","text":""},{"location":"examples/otelib/","title":"Use OTEAPI-OPTIMADE with OTElib","text":"In\u00a0[1]: Copied!elements HAS ALL \"Si\",\"O\" AND nelements<=4\n
from otelib import OTEClient\n\nclient = OTEClient(\"http://localhost:80\")\nfrom otelib import OTEClient client = OTEClient(\"http://localhost:80\") In\u00a0[2]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE\",\n accessUrl=\"https://optimade.materialsproject.org/\",\n)\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE\", accessUrl=\"https://optimade.materialsproject.org/\", )For the filter strategy, we need to know the OPTIMADE query that we want to execute.
For retrieving all structures with the formula
Al2O3
, we can use the following OPTIMADE filter query:In\u00a0[3]: Copied!chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"\n
filter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"',\n)\nfilter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', )Now we can create the OTE pipeline shown above and execute it.
In\u00a0[4]: Copied!pipeline = filter_strategy >> data_resource_strategy\nsession = pipeline.get()\npipeline = filter_strategy >> data_resource_strategy session = pipeline.get()The returned session is a JSON object we can parse and investigate.
In\u00a0[5]: Copied!import json\n\nparsed_session: dict = json.loads(session)\nprint(parsed_session.keys())\nimport json parsed_session: dict = json.loads(session) print(parsed_session.keys())dict_keys(['optimade_resources', 'optimade_config', 'optimade_resource_model'])\nAs can be seen, there are three keys in the returned session.
In\u00a0[6]: Copied!optimade_config
summarizes the query that has been performed to Materials Project. The OPTIMADE structures are listed under theoptimade_resources
. It is named as such due to there being different OPTIMADE resources, e.g.,structures
,references
,links
, etc. The OPTIMADE Python tools has a useful OPTIMADE Structure model class that can be used to parse the OPTIMADE structures into Python objects as well as validating them according to the OPTIMADE specification. Again, since one can query for different OPTIMADE resources, the specific Python class to use is given inoptimade_resource_model
.from importlib import import_module\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\n\nparsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\")\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")\nfrom importlib import import_module import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) parsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\") print(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")The query resulted in 20 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1228448', 'mp-755483', 'mp-1245081', 'mp-1244878', 'mp-1105018', 'mp-754401', 'mp-1245063', 'mp-1245265', 'mp-684591', 'mp-1244898', 'mp-1245211', 'mp-1245056', 'mp-642363', 'mp-1244930', 'mp-1244937', 'mp-1244967', 'mp-1244954', 'mp-1244874', 'mp-1245008', 'mp-1245023']\nTo find them on the Materials Project website, go to
materialsproject.org/materials/<ID>
, for example: materialsproject.org/materials/mp-1228448.What is more, we can investigate the structure according to the well-defined OPTIMADE structure model attributes. For example, so assert the chemical formula is what we expected, we can check the different chemical formula attributes:
In\u00a0[7]: Copied!structure = parsed_structures[0]\nprint(structure.id)\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nstructure = parsed_structures[0] print(structure.id) for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")mp-1228448\nchemical_formula_descriptive: Al2O3\nchemical_formula_reduced: Al2O3\nchemical_formula_hill: Al2O3\nchemical_formula_anonymous: A3B2\nIn\u00a0[8]: Copied!filter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='elements HAS ALL \"Al\",\"O\"',\n limit=5,\n)\nfilter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='elements HAS ALL \"Al\",\"O\"', limit=5, )For this query, we have added the
limit
parameter to the filter configuration, which will pass apage_limit
query parameter to the OPTIMADE API ensuring that we only retrieve the first 5 structures (it limits each result's page to 5 resources).Let us investigate the result again, checking the list of elements and the chemical formula attributes:
In\u00a0[9]: Copied!pipeline = filter_strategy >> data_resource_strategy\nparsed_session = json.loads(pipeline.get())\nprint(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\")\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\nstructures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in structures]}\")\npipeline = filter_strategy >> data_resource_strategy parsed_session = json.loads(pipeline.get()) print(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\") import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"Their Materials Project IDs are: {[structure.id for structure in structures]}\")The query resulted in 5 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1038042', 'mp-1182891', 'mp-1208627', 'mp-1247835', 'mp-1521059']\nLet us also check the query parameters used for the request to the OPTIMADE API to ensure that the
In\u00a0[10]: Copied!page_limit
query parameter was passed:parsed_session[\"optimade_config\"]\nparsed_session[\"optimade_config\"] Out[10]:{'query_parameters': {'filter': 'elements HAS ALL \"Al\",\"O\"', 'page_limit': 5}}In\u00a0[11]: Copied!structure = structures[0]\nprint(structure.id)\nprint(f\"elements: {structure.elements}\")\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nstructure = structures[0] print(structure.id) print(f\"elements: {structure.elements}\") for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")mp-1038042\nelements: ['Al', 'Cr', 'Mg', 'O']\nchemical_formula_descriptive: AlCrMg30O32\nchemical_formula_reduced: AlCrMg30O32\nchemical_formula_hill: AlCrMg30O32\nchemical_formula_anonymous: A32B30CD\nIn\u00a0[12]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE\",\n accessUrl=\"https://aiida.materialscloud.org/mc3d/optimade\",\n)\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE\", accessUrl=\"https://aiida.materialscloud.org/mc3d/optimade\", ) In\u00a0[13]: Copied!pipeline = filter_strategy >> data_resource_strategy\nparsed_session = json.loads(pipeline.get())\nprint(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\")\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\nstructures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"Their Materials Cloud (AiiDA) IDs are: {[structure.id for structure in structures]}\")\npipeline = filter_strategy >> data_resource_strategy parsed_session = json.loads(pipeline.get()) print(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\") import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"Their Materials Cloud (AiiDA) IDs are: {[structure.id for structure in structures]}\")The query resulted in 5 structures found (on page 1) of the returned data.\nTheir Materials Cloud (AiiDA) IDs are: ['13952', '43703', '43800', '56101', '56270']\nAgain, let's check the query parameters used for the request to the OPTIMADE API to ensure it is equivalent to the previous search:
In\u00a0[14]: Copied!parsed_session[\"optimade_config\"]\nparsed_session[\"optimade_config\"] Out[14]:{'query_parameters': {'filter': 'elements HAS ALL \"Al\",\"O\"', 'page_limit': 5}}In\u00a0[15]: Copied!structure = structures[0]\nprint(structure.id)\nprint(f\"elements: {structure.elements}\")\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nstructure = structures[0] print(structure.id) print(f\"elements: {structure.elements}\") for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")13952\nelements: ['Al', 'O']\nchemical_formula_descriptive: Al2O6\nchemical_formula_reduced: AlO3\nchemical_formula_hill: Al2O6\nchemical_formula_anonymous: A3B\nThe MC3D structures can, unfortunately, not be found easily in the Materials Cloud website, as the ID in the DISCOVER section does not match the ID in the OPTIMADE structure. However, the full OPTIMADE structure can be found at
<OPTIMADE_BASE_URL>/structures/<ID>
, for example: aiida.materialscloud.org/mc3d/optimade/structures/13952.Note
The structure with the OPTIMADE ID 13952 is found with the Materials Cloud DISCOVER ID
In\u00a0[16]: Copied!mc3d-76896
and can be found here.client = OTEClient(\"python\")\nclient = OTEClient(\"python\")Now we can go through the same searches as we did with the HTTP requests-based backend. The result should not change.
In\u00a0[17]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE\",\n accessUrl=\"https://optimade.materialsproject.org/\",\n)\n\nfilter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"',\n)\n\npipeline = filter_strategy >> data_resource_strategy\nsession = pipeline.get()\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE\", accessUrl=\"https://optimade.materialsproject.org/\", ) filter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', ) pipeline = filter_strategy >> data_resource_strategy session = pipeline.get()Setting filter from query.\nSetting filter from query.\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', response_format='json', email_address='', response_fields='', sort='', page_limit=20, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', response_format='json', email_address='', response_fields='', sort='', page_limit=20, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\noptimade_query after update: OPTIMADEQueryParameters(filter='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', response_format='json', email_address='', response_fields='', sort='', page_limit=20, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', response_format='json', email_address='', response_fields='', sort='', page_limit=20, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=chemical_formula_descriptive%20%3D%20%22Al2O3%22%20OR%20chemical_formula_reduced%20%3D%20%22Al2O3%22%20OR%20chemical_formula_hill%20%3D%20%22Al2O3%22&response_format=json&page_limit=20&include=references\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=chemical_formula_descriptive%20%3D%20%22Al2O3%22%20OR%20chemical_formula_reduced%20%3D%20%22Al2O3%22%20OR%20chemical_formula_hill%20%3D%20%22Al2O3%22&response_format=json&page_limit=20&include=references\n/home/cwa/.venvs/oteapi-optimade/lib/python3.9/site-packages/optimade/server/config.py:113: UserWarning: Unable to find config file at /home/cwa/.optimade.json, using the default settings instead.\n warnings.warn(\nAs one can see, this backend runs locally within the same Python environment as the notebook. This is useful for development purposes, where the logging messages are shown directly in the output.
In\u00a0[18]: Copied!parsed_session: dict = json.loads(session)\nprint(parsed_session.keys())\nparsed_session: dict = json.loads(session) print(parsed_session.keys())dict_keys(['optimade_config', 'optimade_resources', 'optimade_resource_model'])\nWe get the same keys in the returned session as we did with the HTTP requests-based backend. If we again import the OPTIMADE Structure model class from the OPTIMADE Python tools, we can parse the OPTIMADE structures into Python objects as well as validating them according to the OPTIMADE specification, just as we did with the HTTP requests-based backend.
In\u00a0[19]: Copied!import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\n\nparsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\")\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")\n\nstructure = parsed_structures[0]\nprint(structure.id)\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) parsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\") print(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\") structure = parsed_structures[0] print(structure.id) for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")The query resulted in 20 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1228448', 'mp-755483', 'mp-1245081', 'mp-1244878', 'mp-1105018', 'mp-754401', 'mp-1245063', 'mp-1245265', 'mp-684591', 'mp-1244898', 'mp-1245211', 'mp-1245056', 'mp-642363', 'mp-1244930', 'mp-1244937', 'mp-1244967', 'mp-1244954', 'mp-1244874', 'mp-1245008', 'mp-1245023']\nmp-1228448\nchemical_formula_descriptive: Al2O3\nchemical_formula_reduced: Al2O3\nchemical_formula_hill: Al2O3\nchemical_formula_anonymous: A3B2\nIn\u00a0[20]: Copied!filter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='elements HAS ALL \"Al\",\"O\"',\n limit=5,\n)\n\npipeline = filter_strategy >> data_resource_strategy\nparsed_session = json.loads(pipeline.get())\nprint(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\")\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\nstructures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in structures]}\")\nfilter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='elements HAS ALL \"Al\",\"O\"', limit=5, ) pipeline = filter_strategy >> data_resource_strategy parsed_session = json.loads(pipeline.get()) print(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\") import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"Their Materials Project IDs are: {[structure.id for structure in structures]}\")Setting filter from query.\nSetting filter from query.\nSetting filter from query.\nSetting page_limit from limit.\nSetting page_limit from limit.\nSetting page_limit from limit.\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nThe query resulted in 5 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1038042', 'mp-1182891', 'mp-1208627', 'mp-1247835', 'mp-1521059']\nThe configuration in
In\u00a0[21]: Copied!optimade_config
is quite more extensive than with the HTTP requests-based backend, as it includes more information, automatically setting the default values so they are shown in the config:parsed_session[\"optimade_config\"]\nparsed_session[\"optimade_config\"] Out[21]:{'version': 'v1',\n 'endpoint': 'structures',\n 'query_parameters': {'filter': 'elements HAS ALL \"Al\",\"O\"', 'page_limit': 5},\n 'datacache_config': {'cacheDir': 'oteapi',\n 'accessKey': None,\n 'hashType': 'md5',\n 'expireTime': 86400,\n 'tag': 'optimade'},\n 'return_object': False,\n 'use_dlite': False}Let's look at the first structure again:
In\u00a0[22]: Copied!structure = structures[0]\nprint(structure.id)\nprint(f\"elements: {structure.elements}\")\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nstructure = structures[0] print(structure.id) print(f\"elements: {structure.elements}\") for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")mp-1038042\nelements: ['Al', 'Cr', 'Mg', 'O']\nchemical_formula_descriptive: AlCrMg30O32\nchemical_formula_reduced: AlCrMg30O32\nchemical_formula_hill: AlCrMg30O32\nchemical_formula_anonymous: A32B30CD\nIn\u00a0[23]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE\",\n accessUrl=\"https://aiida.materialscloud.org/mc3d/optimade\",\n)\n\npipeline = filter_strategy >> data_resource_strategy\nparsed_session = json.loads(pipeline.get())\nprint(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\")\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\nstructures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"Their Materials Cloud (AiiDA) IDs are: {[structure.id for structure in structures]}\")\n\nprint(parsed_session[\"optimade_config\"])\n\nstructure = structures[0]\nprint(structure.id)\nprint(f\"elements: {structure.elements}\")\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE\", accessUrl=\"https://aiida.materialscloud.org/mc3d/optimade\", ) pipeline = filter_strategy >> data_resource_strategy parsed_session = json.loads(pipeline.get()) print(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\") import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"Their Materials Cloud (AiiDA) IDs are: {[structure.id for structure in structures]}\") print(parsed_session[\"optimade_config\"]) structure = structures[0] print(structure.id) print(f\"elements: {structure.elements}\") for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")Setting filter from query.\nSetting filter from query.\nSetting filter from query.\nSetting page_limit from limit.\nSetting page_limit from limit.\nSetting page_limit from limit.\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://aiida.materialscloud.org/mc3d/optimade', base_url='https://aiida.materialscloud.org/mc3d/optimade', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://aiida.materialscloud.org/mc3d/optimade', base_url='https://aiida.materialscloud.org/mc3d/optimade', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://aiida.materialscloud.org/mc3d/optimade', base_url='https://aiida.materialscloud.org/mc3d/optimade', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\nOPTIMADE URL to be requested: https://aiida.materialscloud.org/mc3d/optimade/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nOPTIMADE URL to be requested: https://aiida.materialscloud.org/mc3d/optimade/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nOPTIMADE URL to be requested: https://aiida.materialscloud.org/mc3d/optimade/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nThe query resulted in 5 structures found (on page 1) of the returned data.\nTheir Materials Cloud (AiiDA) IDs are: ['13952', '43703', '43800', '56101', '56270']\n{'version': 'v1', 'endpoint': 'structures', 'query_parameters': {'filter': 'elements HAS ALL \"Al\",\"O\"', 'page_limit': 5}, 'datacache_config': {'cacheDir': 'oteapi', 'accessKey': None, 'hashType': 'md5', 'expireTime': 86400, 'tag': 'optimade'}, 'return_object': False, 'use_dlite': False}\n13952\nelements: ['Al', 'O']\nchemical_formula_descriptive: Al2O6\nchemical_formula_reduced: AlO3\nchemical_formula_hill: Al2O6\nchemical_formula_anonymous: A3B\n"},{"location":"examples/otelib/#use-oteapi-optimade-with-otelib","title":"Use OTEAPI-OPTIMADE with OTElib\u00b6","text":"OTElib is a Python client library for the OTEAPI system. It has two usable backends:
- Python-based. This uses the OTEAPI Core library directly to exectue pipelines. This backend should mainly be used for development purposes.
- HTTP requests-based. This uses the OTEAPI Services library to execute pipelines via HTTP requests to a running OTEAPI server. This backend should be used for production purposes.
This notebook demonstrates how to use OTEAPI-OPTIMADE with OTElib.
"},{"location":"examples/otelib/#example","title":"Example\u00b6","text":"Using OTElib we will do different OPTIMADE queries. First, we will search through the Materials Project database for all structures with the formula
"},{"location":"examples/otelib/#http-requests-based-backend","title":"HTTP requests-based backend\u00b6","text":"Al2O3
. Then, we will search through the Materials Project database for all structures that includeAl
andO
in their chemical formula. Finally, we will search through the Materials Cloud MC3D - Materials Cloud three-dimensional crystals database for all structures that includeAl
andO
in their chemical formula.This backend is equivalent to running in production. It requires a running OTEAPI server.
"},{"location":"examples/otelib/#setup","title":"Setup\u00b6","text":"First, we need to have a running OTEAPI server. If one is not available, we can start one using Docker. See the overview for instructions on how to start a server.
Further documentation about the OTEAPI Service is available as a README on GitHub.
Note
It is advisable to run the OTEAPI server in a separate terminal window, so that we can see the logs from the server. Furthermore, we can stop the server by pressing
Ctrl+C
in the terminal window.After following the instructions, we should have a running OTEAPI server at localhost:80.
"},{"location":"examples/otelib/#create-a-client","title":"Create a client\u00b6","text":""},{"location":"examples/otelib/#search-through-materials-project-for-all-structures-with-the-formula-al2o3","title":"Search through Materials Project for all structures with the formulaAl2O3
\u00b6","text":"To search through Materials Project for all structures with the formula
Al2O3
, we need to create the OTE strategies that we want to use for creating the OTE pipeline:To create the strategies, we need to know how to configure them. This information is available in the OTEAPI Core documentation, specifically for Data Resource strategies and for Filter strategies.
For the data resource strategy, i.e., the OPTIMADE DB strategy, we need to know the base URL of the OPTIMADE API for Materials Project. OPTIMADE has a useful providers dashboard that lists all known OPTIMADE providers and their (sub-)databases. Here we can find the base URL for the Materials Project:
"},{"location":"examples/otelib/#search-through-materials-project-for-all-structures-that-include-al-and-o-in-their-chemical-formula","title":"Search through Materials Project for all structures that includehttps://optimade.materialsproject.org
.Al
andO
in their chemical formula\u00b6","text":"We must use a different OPTIMADE filter query for this search:
elements HAS ALL \"Al\",\"O\"\n
elements
is a structure attribute that lists all elements in the structure. TheHAS ALL
operator matches if, for each value, there is at least one element inelements
equal to that value. (This implements the set operator>=
.)To do this search, we can reuse the OTE pipeline from the previous search, but change the filter strategy.
"},{"location":"examples/otelib/#search-through-mc3d-database-in-materials-cloud-for-all-structures-that-include-al-and-o-in-their-chemical-formula","title":"Search through MC3D database in Materials Cloud for all structures that includeAl
andO
in their chemical formula\u00b6","text":"Finally, let us reuse the same filter strategy we used for the previous search, but change the data resource strategy to point to the MC3D database in Materials Cloud.
The base URL for the MC3D database is
"},{"location":"examples/otelib/#python-based-backend","title":"Python-based backend\u00b6","text":"https://aiida.materialscloud.org/mc3d/optimade
as found in the providers dashboard.With this backend, we do not need a running OTEAPI server.
"},{"location":"examples/otelib/#create-a-client","title":"Create a client\u00b6","text":""},{"location":"examples/otelib/#search-through-materials-project-for-all-structures-with-the-formula-al2o3","title":"Search through Materials Project for all structures with the formulaAl2O3
\u00b6","text":""},{"location":"examples/otelib/#search-through-materials-project-for-all-structures-that-include-al-and-o-in-their-chemical-formula","title":"Search through Materials Project for all structures that includeAl
andO
in their chemical formula\u00b6","text":""},{"location":"examples/otelib/#search-through-mc3d-database-in-materials-cloud-for-all-structures-that-include-al-and-o-in-their-chemical-formula","title":"Search through MC3D database in Materials Cloud for all structures that includeAl
andO
in their chemical formula\u00b6","text":""}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"OTE-API OPTIMADE","text":"An OTE-API Plugin with OTE strategies.
Further reading:
"},{"location":"#license-and-copyright","title":"License and copyright","text":"
- OTE-API Core Documentation
- OTE-API Services Documentation
OTE-API OPTIMADE is released under the MIT license with copyright \u00a9 SINTEF.
"},{"location":"#acknowledgment","title":"Acknowledgment","text":"OTE-API OPTIMADE has been created via the cookiecutter template for OTE-API plugins.
OTE-API OPTIMADE has been supported by the following projects:
"},{"location":"CHANGELOG/","title":"Changelog","text":""},{"location":"CHANGELOG/#v041-2023-10-26","title":"v0.4.1 (2023-10-26)","text":"
OntoTrans (2020-2024) that receives funding from the European Union\u2019s Horizon 2020 Research and Innovation Programme, under Grant Agreement n. 862136.
VIPCOAT (2021-2025) receives funding from the European Union\u2019s Horizon 2020 Research and Innovation Programme - DT-NMBP-11-2020 Open Innovation Platform for Materials Modelling, under Grant Agreement no: 952903.
OpenModel (2021-2025) receives funding from the European Union\u2019s Horizon 2020 Research and Innovation Programme - DT-NMBP-11-2020 Open Innovation Platform for Materials Modelling, under Grant Agreement no: 953167.
Full Changelog
Fixed bugs:
- Invalid use of logging #174
- OPTIMADE plugin produces empty instances of http://onto-ns.com/meta/1.0/OPTIMADEStructureSpecies #162
Merged pull requests:
"},{"location":"CHANGELOG/#v040-2023-10-23","title":"v0.4.0 (2023-10-23)","text":"
- Properly create assemblies and species #172 (CasperWA)
- Proper use of logging #171 (jesper-friis)
Full Changelog
Implemented enhancements:
- Add example(s) #124
Fixed bugs:
- Wrong OPTIMADEStructureAttributes datamodel #164
- Pipeline figure not being shown in docs #144
- Updated DLite installation pathway #136
Segmentation fault
from dlite in CI #115- init file missing in the new
dlite
module #113Closed issues:
- Make the JSON-serialisation of entities human readable #160
- Use ruff instead of pylint (and isort) #156
Merged pull requests:
"},{"location":"CHANGELOG/#v030-2023-03-30","title":"v0.3.0 (2023-03-30)","text":"
- Write \u00c5ngstr\u00f6m such that it is understandable by Pint in datamodel #170 (jesper-friis)
- Update data models #169 (CasperWA)
- Move from pylint (& isort) to ruff #157 (CasperWA)
- Use relative link, which works only in production #145 (CasperWA)
- Avoid DLite v0.4.0 #139 (CasperWA)
- DLite notebook example #127 (CasperWA)
- Add example to documentation #125 (CasperWA)
- Add __init__ file to dlite submodule #122 (CasperWA)
- Avoid psycopg2-binary v2.9.6 #117 (CasperWA)
Full Changelog
Implemented enhancements:
- Use
SINTEF/ci-cd
CI - Tests workflow #71- Implement support for DLite #31
Fixed bugs:
- Fix CI/CD workflows for external usage #84
- Update to
SINTEF/ci-cd
instead ofCasperWA/ci-cd
#72Closed issues:
- Use SINTEF/ci-cd v2 #104
- Reinstate pre-commit hooks for docs #68
Merged pull requests:
"},{"location":"CHANGELOG/#v022-2022-07-06","title":"v0.2.2 (2022-07-06)","text":"
- Support DLite #109 (CasperWA)
- Update to SINTEF/ci-cd v2 #105 (CasperWA)
- Update input keywords for SINTEF/ci-cd workflows #85 (CasperWA)
- Use CasperWA/ci-cd pre-commit hooks #69 (CasperWA)
Full Changelog
Implemented enhancements:
- Update to use all workflows from CasperWA/gh-actions #63 (CasperWA)
Fixed bugs:
- New workflow is removing API reference in documentation #64
Closed issues:
- Update to new repository name for callable workflows #66
Merged pull requests:
"},{"location":"CHANGELOG/#v021-2022-07-01","title":"v0.2.1 (2022-07-01)","text":"
- Use new repo name for callable workflows repo #67 (CasperWA)
- Properly create API reference and clean up #65 (CasperWA)
Full Changelog
Closed issues:
- Set
test: false
for publish workflow #61Merged pull requests:
"},{"location":"CHANGELOG/#v021-alpha1-2022-07-01","title":"v0.2.1-alpha.1 (2022-07-01)","text":"
- Don't run publish workflow as a test #62 (CasperWA)
Full Changelog
Implemented enhancements:
- Auto-merge generated PR from new workflow #49
- Properly update dependencies #46
- Use CasperWA/gh-actions workflows #60 (CasperWA)
Fixed bugs:
- New workflow failing #48
Merged pull requests:
"},{"location":"CHANGELOG/#v020-2022-05-18","title":"v0.2.0 (2022-05-18)","text":"
- [Auto-generated] Update dependencies #53 (TEAM4-0)
- [Auto-generated] Update dependencies #52 (TEAM4-0)
- Auto-merge new CD workflow-generated PR #50 (CasperWA)
- New CD workflow to update dependencies in pyproject.toml #47 (CasperWA)
- [Auto-generated] Update dependencies #44 (TEAM4-0)
Full Changelog
Implemented enhancements:
- Implement OPTIMADE filter strategy #4
Fixed bugs:
- CI docker connection issues #34
Closed issues:
- Use the
optimade
container image in CI #41- Extend acknowledgements in README #38
Merged pull requests:
"},{"location":"CHANGELOG/#v010-2022-03-29","title":"v0.1.0 (2022-03-29)","text":"
- Use the optimade container image in CI #42 (CasperWA)
- [Auto-generated] Update dependencies #40 (TEAM4-0)
- Add VIPCOAT and OpenModel to README ack #39 (CasperWA)
- Fix real backend CI job #37 (CasperWA)
- [Auto-generated] Update dependencies #36 (TEAM4-0)
- Add a Filter strategy #33 (CasperWA)
Full Changelog
Implemented enhancements:
- Correctly handle trailing slashes (
/
) #28Merged pull requests:
"},{"location":"CHANGELOG/#v002-2022-03-29","title":"v0.0.2 (2022-03-29)","text":"
- Trailing slash in base URL #29 (CasperWA)
Full Changelog
Implemented enhancements:
- Implement OPTIMADE parse strategy #5
- Implement OPTIMADE resource strategy #3
Fixed bugs:
- Fix CI connection refusal for pytest-real-backend job #26
- CD workflow failing - flit not building #23
- Black issue with click #21
- CD workflow failing #18
- GH GraphQL type issue in auto-merge workflow #6
- Fix CI #1
Closed issues:
- CI test with end-to-end #17
Merged pull requests:
- Fix pytest-real-backend CI job #27 (CasperWA)
- Test release workflow #25 (CasperWA)
- Build package prior to polluting git tree #24 (CasperWA)
- Update pre-commit hooks #22 (CasperWA)
- Fix failing release workflow #20 (CasperWA)
- Setup CI end-to-end test #19 (CasperWA)
- [Auto-generated] Update dependencies #15 (TEAM4-0)
- [Auto-generated] Update dependencies #13 (TEAM4-0)
- Implement an OPTIMADE Resource strategy #12 (CasperWA)
- [Auto-generated] Update dependencies #11 (TEAM4-0)
- [Auto-generated] Update dependencies #10 (TEAM4-0)
- Use
ID!
type instead ofString!
#7 (CasperWA)- Fix CI and use flit #2 (CasperWA)
* This Changelog was automatically generated by github_changelog_generator
"},{"location":"LICENSE/","title":"License","text":"MIT License
Copyright (c) 2022 SINTEF
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"},{"location":"all_strategies/","title":"OTE-API OPTIMADE Strategies","text":"This page provides documentation for the
oteapi_optimade.strategies
submodule, where all the OTE-API OPTIMADE strategies are located.These strategies will be available when setting up a server in an environment with oteapi-optimade installed.
"},{"location":"all_strategies/#oteapi_optimade.strategies.filter","title":"filter
","text":"Demo filter strategy.
"},{"location":"all_strategies/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy","title":"OPTIMADEFilterStrategy
dataclass
","text":"Filter Strategy.
Implements strategies:
Source code in
(\"filterType\", \"OPTIMADE\")
(\"filterType\", \"optimade\")
(\"filterType\", \"OPTiMaDe\")
oteapi_optimade/strategies/filter.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy.get","title":"@dataclass\nclass OPTIMADEFilterStrategy:\n \"\"\"Filter Strategy.\n\n **Implements strategies**:\n\n - `(\"filterType\", \"OPTIMADE\")`\n - `(\"filterType\", \"optimade\")`\n - `(\"filterType\", \"OPTiMaDe\")`\n\n \"\"\"\n\n filter_config: OPTIMADEFilterConfig\n\n def initialize(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEFilterSession:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Configuration values, specifically URL query parameters, can be provided to the\n OPTIMADE resource strategy through this filter strategy.\n\n Workflow:\n\n 1. Compile received information.\n 2. Update session with compiled information.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEFilterSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEFilterSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEFilterSession()\n\n if session.optimade_config:\n self.filter_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_config = self.filter_config.configuration.copy()\n\n if not optimade_config.query_parameters:\n optimade_config.query_parameters = OPTIMADEQueryParameters()\n\n if self.filter_config.query:\n LOGGER.debug(\"Setting filter from query.\")\n optimade_config.query_parameters.filter = self.filter_config.query\n\n if self.filter_config.limit:\n LOGGER.debug(\"Setting page_limit from limit.\")\n optimade_config.query_parameters.page_limit = self.filter_config.limit\n\n return session.copy(\n update={\n \"optimade_config\": optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n },\n )\n\n def get(\n self,\n session: \"Optional[Dict[str, Any]]\" = None,\n ) -> SessionUpdate:\n \"\"\"Execute the strategy.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n return SessionUpdate()\n
get(self, session=None)
","text":"Execute the strategy.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/filter.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy.initialize","title":"def get(\n self,\n session: \"Optional[Dict[str, Any]]\" = None,\n) -> SessionUpdate:\n \"\"\"Execute the strategy.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n return SessionUpdate()\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Configuration values, specifically URL query parameters, can be provided to the OPTIMADE resource strategy through this filter strategy.
Workflow:
- Compile received information.
- Update session with compiled information.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary context.
None
Returns:
Type DescriptionOPTIMADEFilterSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/filter.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.parse","title":"def initialize(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEFilterSession:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Configuration values, specifically URL query parameters, can be provided to the\n OPTIMADE resource strategy through this filter strategy.\n\n Workflow:\n\n 1. Compile received information.\n 2. Update session with compiled information.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEFilterSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEFilterSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEFilterSession()\n\n if session.optimade_config:\n self.filter_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_config = self.filter_config.configuration.copy()\n\n if not optimade_config.query_parameters:\n optimade_config.query_parameters = OPTIMADEQueryParameters()\n\n if self.filter_config.query:\n LOGGER.debug(\"Setting filter from query.\")\n optimade_config.query_parameters.filter = self.filter_config.query\n\n if self.filter_config.limit:\n LOGGER.debug(\"Setting page_limit from limit.\")\n optimade_config.query_parameters.page_limit = self.filter_config.limit\n\n return session.copy(\n update={\n \"optimade_config\": optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n },\n )\n
parse
","text":"Demo strategy class for text/json.
"},{"location":"all_strategies/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy","title":"OPTIMADEParseStrategy
dataclass
","text":"Parse strategy for JSON.
Implements strategies:
Source code in
(\"mediaType\", \"application/vnd.optimade+json\")
(\"mediaType\", \"application/vnd.OPTIMADE+json\")
(\"mediaType\", \"application/vnd.OPTiMaDe+json\")
(\"mediaType\", \"application/vnd.optimade+JSON\")
(\"mediaType\", \"application/vnd.OPTIMADE+JSON\")
(\"mediaType\", \"application/vnd.OPTiMaDe+JSON\")
(\"mediaType\", \"application/vnd.optimade\")
(\"mediaType\", \"application/vnd.OPTIMADE\")
(\"mediaType\", \"application/vnd.OPTiMaDe\")
oteapi_optimade/strategies/parse.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy.get","title":"@dataclass\nclass OPTIMADEParseStrategy:\n \"\"\"Parse strategy for JSON.\n\n **Implements strategies**:\n\n - `(\"mediaType\", \"application/vnd.optimade+json\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+json\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+json\")`\n - `(\"mediaType\", \"application/vnd.optimade+JSON\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+JSON\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+JSON\")`\n - `(\"mediaType\", \"application/vnd.optimade\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe\")`\n\n \"\"\"\n\n parse_config: OPTIMADEParseConfig\n\n def initialize(self, session: \"Optional[Dict[str, Any]]\" = None) -> SessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return SessionUpdate()\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEParseSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEParseSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEParseSession()\n\n if session.optimade_config:\n self.parse_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n cache = DataCache(self.parse_config.configuration.datacache_config)\n if self.parse_config.downloadUrl in cache:\n response: \"Dict[str, Any]\" = cache.get(self.parse_config.downloadUrl)\n elif (\n self.parse_config.configuration.datacache_config.accessKey\n and self.parse_config.configuration.datacache_config.accessKey in cache\n ):\n response = cache.get(\n self.parse_config.configuration.datacache_config.accessKey\n )\n else:\n download_config = self.parse_config.copy()\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n response = {\"json\": json.loads(cache.get(session.pop(\"key\")))}\n\n if (\n not response.get(\"ok\", True)\n or (\n 200 > response.get(\"status_code\", 200)\n or response.get(\"status_code\", 200) >= 300\n )\n or \"errors\" in response.get(\"json\", {})\n ):\n # Error response\n try:\n response_object = ErrorResponse(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Could not validate an error response.\\nValidationError: \"\n \"%s\\nresponse=%r\",\n exc,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate an error response.\"\n ) from exc\n else:\n # Successful response\n response_model = self.parse_config.downloadUrl.response_model()\n if response_model:\n if not isinstance(response_model, tuple):\n response_model = (response_model,)\n for model_cls in response_model:\n try:\n response_object = model_cls(**response.get(\"json\", {}))\n except ValidationError:\n pass\n else:\n break\n else:\n LOGGER.error(\n \"Could not validate for an expected response model.\\nURL=%r\\n\"\n \"response_models=%r\\nresponse=%s\",\n self.parse_config.downloadUrl,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate for an expected response model.\"\n )\n else:\n # No \"endpoint\" or unknown\n try:\n response_object = Success(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Unknown or unparseable endpoint.\\nValidatonError: %s\\n\"\n \"URL=%r\\nendpoint=%r\\nresponse_model=%r\\nresponse=%s\",\n exc,\n self.parse_config.downloadUrl,\n self.parse_config.downloadUrl.endpoint,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Unknown or unparseable endpoint.\"\n ) from exc\n\n if self.parse_config.configuration.return_object:\n session.optimade_response_object = response_object\n else:\n session.optimade_response = model2dict(response_object)\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
get(self, session=None)
","text":"Request and parse an OPTIMADE response using OPT.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromdownloadUrl
.Workflow:
- Request OPTIMADE response.
- Parse as an OPTIMADE Python tools (OPT) pydantic response model.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEParseSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/parse.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEParseSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEParseSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEParseSession()\n\n if session.optimade_config:\n self.parse_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n cache = DataCache(self.parse_config.configuration.datacache_config)\n if self.parse_config.downloadUrl in cache:\n response: \"Dict[str, Any]\" = cache.get(self.parse_config.downloadUrl)\n elif (\n self.parse_config.configuration.datacache_config.accessKey\n and self.parse_config.configuration.datacache_config.accessKey in cache\n ):\n response = cache.get(\n self.parse_config.configuration.datacache_config.accessKey\n )\n else:\n download_config = self.parse_config.copy()\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n response = {\"json\": json.loads(cache.get(session.pop(\"key\")))}\n\n if (\n not response.get(\"ok\", True)\n or (\n 200 > response.get(\"status_code\", 200)\n or response.get(\"status_code\", 200) >= 300\n )\n or \"errors\" in response.get(\"json\", {})\n ):\n # Error response\n try:\n response_object = ErrorResponse(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Could not validate an error response.\\nValidationError: \"\n \"%s\\nresponse=%r\",\n exc,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate an error response.\"\n ) from exc\n else:\n # Successful response\n response_model = self.parse_config.downloadUrl.response_model()\n if response_model:\n if not isinstance(response_model, tuple):\n response_model = (response_model,)\n for model_cls in response_model:\n try:\n response_object = model_cls(**response.get(\"json\", {}))\n except ValidationError:\n pass\n else:\n break\n else:\n LOGGER.error(\n \"Could not validate for an expected response model.\\nURL=%r\\n\"\n \"response_models=%r\\nresponse=%s\",\n self.parse_config.downloadUrl,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate for an expected response model.\"\n )\n else:\n # No \"endpoint\" or unknown\n try:\n response_object = Success(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Unknown or unparseable endpoint.\\nValidatonError: %s\\n\"\n \"URL=%r\\nendpoint=%r\\nresponse_model=%r\\nresponse=%s\",\n exc,\n self.parse_config.downloadUrl,\n self.parse_config.downloadUrl.endpoint,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Unknown or unparseable endpoint.\"\n ) from exc\n\n if self.parse_config.configuration.return_object:\n session.optimade_response_object = response_object\n else:\n session.optimade_response = model2dict(response_object)\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/parse.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource","title":"def initialize(self, session: \"Optional[Dict[str, Any]]\" = None) -> SessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return SessionUpdate()\n
resource
","text":"OPTIMADE resource strategy.
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy","title":"OPTIMADEResourceStrategy
dataclass
","text":"OPTIMADE Resource Strategy.
Implements strategies:
Source code in
(\"accessService\", \"optimade\")
(\"accessService\", \"OPTIMADE\")
(\"accessService\", \"OPTiMaDe\")
(\"accessService\", \"optimade+dlite\")
(\"accessService\", \"OPTIMADE+dlite\")
(\"accessService\", \"OPTiMaDe+dlite\")
(\"accessService\", \"optimade+DLite\")
(\"accessService\", \"OPTIMADE+DLite\")
(\"accessService\", \"OPTiMaDe+DLite\")
oteapi_optimade/strategies/resource.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy.get","title":"@dataclass\nclass OPTIMADEResourceStrategy:\n \"\"\"OPTIMADE Resource Strategy.\n\n **Implements strategies**:\n\n - `(\"accessService\", \"optimade\")`\n - `(\"accessService\", \"OPTIMADE\")`\n - `(\"accessService\", \"OPTiMaDe\")`\n - `(\"accessService\", \"optimade+dlite\")`\n - `(\"accessService\", \"OPTIMADE+dlite\")`\n - `(\"accessService\", \"OPTiMaDe+dlite\")`\n - `(\"accessService\", \"optimade+DLite\")`\n - `(\"accessService\", \"OPTIMADE+DLite\")`\n - `(\"accessService\", \"OPTiMaDe+DLite\")`\n\n \"\"\"\n\n resource_config: OPTIMADEResourceConfig\n\n def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n ) -> \"Union[SessionUpdate, DLiteSessionUpdate]\":\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n ):\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n return SessionUpdate()\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEResourceSession:\n \"\"\"Execute an OPTIMADE query to `accessUrl`.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `accessUrl`.\n\n Workflow:\n 1. Update configuration according to session.\n 2. Deconstruct `accessUrl` (done partly by\n `oteapi_optimade.models.custom_types.OPTIMADEUrl`).\n 3. Reconstruct the complete query URL.\n 4. Send query.\n 5. Store result in data cache.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEResourceSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEResourceSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEResourceSession()\n\n if session.optimade_config:\n self.resource_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_endpoint = self.resource_config.accessUrl.endpoint or \"structures\"\n optimade_query = (\n self.resource_config.configuration.query_parameters\n or OPTIMADEQueryParameters()\n )\n LOGGER.debug(\"resource_config: %r\", self.resource_config)\n\n if self.resource_config.accessUrl.query:\n parsed_query = parse_qs(self.resource_config.accessUrl.query)\n for field, value in parsed_query.items():\n # Only use the latest defined value for any parameter\n if field not in optimade_query.__fields_set__:\n LOGGER.debug(\n \"Setting %r from accessUrl (value=%r)\", field, value[-1]\n )\n setattr(optimade_query, field, value[-1])\n\n LOGGER.debug(\"optimade_query after update: %r\", optimade_query)\n\n optimade_url = OPTIMADEUrl(\n f\"{self.resource_config.accessUrl.base_url}\"\n f\"/{self.resource_config.accessUrl.version or 'v1'}\"\n f\"/{optimade_endpoint}?{optimade_query.generate_query_string()}\"\n )\n LOGGER.debug(\"OPTIMADE URL to be requested: %s\", optimade_url)\n\n # Set cache access key to the full OPTIMADE URL.\n self.resource_config.configuration.datacache_config.accessKey = optimade_url\n\n # Perform query\n response = requests.get(\n optimade_url,\n allow_redirects=True,\n timeout=(3, 27), # timeout in seconds (connect, read)\n )\n\n if optimade_query.response_format and optimade_query.response_format != \"json\":\n raise NotImplementedError(\n \"Can only handle JSON responses for now. Requested response format: \"\n f\"{optimade_query.response_format!r}\"\n )\n\n cache = DataCache(config=self.resource_config.configuration.datacache_config)\n cache.add(\n {\n \"status_code\": response.status_code,\n \"ok\": response.ok,\n \"json\": response.json(),\n }\n )\n\n parse_with_dlite = use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n )\n\n parse_mediaType = (\n \"application/vnd.\"\n f\"{self.resource_config.accessService.split('+', maxsplit=1)[0]}\"\n )\n if parse_with_dlite:\n parse_mediaType += \"+DLite\"\n elif optimade_query.response_format:\n parse_mediaType += f\"+{optimade_query.response_format}\"\n\n parse_config = {\n \"downloadUrl\": optimade_url,\n \"mediaType\": parse_mediaType,\n \"configuration\": {\n \"datacache_config\": self.resource_config.configuration.datacache_config,\n \"return_object\": True,\n },\n }\n\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the session.\"\n )\n optimade_response: \"OPTIMADEResponse\" = session.pop(\"optimade_response_object\")\n if \"optimade_response\" in session and not session.get(\"optimade_response\"):\n del session[\"optimade_response\"]\n\n if isinstance(optimade_response, ErrorResponse):\n optimade_resources = optimade_response.errors\n session.optimade_resource_model = (\n f\"{OptimadeError.__module__}:OptimadeError\"\n )\n elif isinstance(optimade_response, ReferenceResponseMany):\n optimade_resources = [\n Reference(entry).as_dict\n if isinstance(entry, dict)\n else Reference(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, ReferenceResponseOne):\n optimade_resources = [\n Reference(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Reference(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, StructureResponseMany):\n optimade_resources = [\n Structure(entry).as_dict\n if isinstance(entry, dict)\n else Structure(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n elif isinstance(optimade_response, StructureResponseOne):\n optimade_resources = [\n Structure(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Structure(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n else:\n LOGGER.debug(\n \"Could not parse response as errors, references or structures. \"\n \"Response:\\n%r\",\n optimade_response,\n )\n raise OPTIMADEParseError(\n \"Could not retrieve errors, references or structures from response \"\n f\"from {optimade_url}. It could be a valid OPTIMADE API response, \"\n \"however it may not be supported by OTEAPI-OPTIMADE. It may also be an \"\n \"invalid response completely.\"\n )\n\n session.optimade_resources = [\n model2dict(resource) for resource in optimade_resources\n ]\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
get(self, session=None)
","text":"Execute an OPTIMADE query to
accessUrl
.This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromaccessUrl
.Workflow: 1. Update configuration according to session. 2. Deconstruct
accessUrl
(done partly byoteapi_optimade.models.custom_types.OPTIMADEUrl
). 3. Reconstruct the complete query URL. 4. Send query. 5. Store result in data cache.Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEResourceSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEResourceSession:\n \"\"\"Execute an OPTIMADE query to `accessUrl`.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `accessUrl`.\n\n Workflow:\n 1. Update configuration according to session.\n 2. Deconstruct `accessUrl` (done partly by\n `oteapi_optimade.models.custom_types.OPTIMADEUrl`).\n 3. Reconstruct the complete query URL.\n 4. Send query.\n 5. Store result in data cache.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEResourceSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEResourceSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEResourceSession()\n\n if session.optimade_config:\n self.resource_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_endpoint = self.resource_config.accessUrl.endpoint or \"structures\"\n optimade_query = (\n self.resource_config.configuration.query_parameters\n or OPTIMADEQueryParameters()\n )\n LOGGER.debug(\"resource_config: %r\", self.resource_config)\n\n if self.resource_config.accessUrl.query:\n parsed_query = parse_qs(self.resource_config.accessUrl.query)\n for field, value in parsed_query.items():\n # Only use the latest defined value for any parameter\n if field not in optimade_query.__fields_set__:\n LOGGER.debug(\n \"Setting %r from accessUrl (value=%r)\", field, value[-1]\n )\n setattr(optimade_query, field, value[-1])\n\n LOGGER.debug(\"optimade_query after update: %r\", optimade_query)\n\n optimade_url = OPTIMADEUrl(\n f\"{self.resource_config.accessUrl.base_url}\"\n f\"/{self.resource_config.accessUrl.version or 'v1'}\"\n f\"/{optimade_endpoint}?{optimade_query.generate_query_string()}\"\n )\n LOGGER.debug(\"OPTIMADE URL to be requested: %s\", optimade_url)\n\n # Set cache access key to the full OPTIMADE URL.\n self.resource_config.configuration.datacache_config.accessKey = optimade_url\n\n # Perform query\n response = requests.get(\n optimade_url,\n allow_redirects=True,\n timeout=(3, 27), # timeout in seconds (connect, read)\n )\n\n if optimade_query.response_format and optimade_query.response_format != \"json\":\n raise NotImplementedError(\n \"Can only handle JSON responses for now. Requested response format: \"\n f\"{optimade_query.response_format!r}\"\n )\n\n cache = DataCache(config=self.resource_config.configuration.datacache_config)\n cache.add(\n {\n \"status_code\": response.status_code,\n \"ok\": response.ok,\n \"json\": response.json(),\n }\n )\n\n parse_with_dlite = use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n )\n\n parse_mediaType = (\n \"application/vnd.\"\n f\"{self.resource_config.accessService.split('+', maxsplit=1)[0]}\"\n )\n if parse_with_dlite:\n parse_mediaType += \"+DLite\"\n elif optimade_query.response_format:\n parse_mediaType += f\"+{optimade_query.response_format}\"\n\n parse_config = {\n \"downloadUrl\": optimade_url,\n \"mediaType\": parse_mediaType,\n \"configuration\": {\n \"datacache_config\": self.resource_config.configuration.datacache_config,\n \"return_object\": True,\n },\n }\n\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the session.\"\n )\n optimade_response: \"OPTIMADEResponse\" = session.pop(\"optimade_response_object\")\n if \"optimade_response\" in session and not session.get(\"optimade_response\"):\n del session[\"optimade_response\"]\n\n if isinstance(optimade_response, ErrorResponse):\n optimade_resources = optimade_response.errors\n session.optimade_resource_model = (\n f\"{OptimadeError.__module__}:OptimadeError\"\n )\n elif isinstance(optimade_response, ReferenceResponseMany):\n optimade_resources = [\n Reference(entry).as_dict\n if isinstance(entry, dict)\n else Reference(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, ReferenceResponseOne):\n optimade_resources = [\n Reference(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Reference(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, StructureResponseMany):\n optimade_resources = [\n Structure(entry).as_dict\n if isinstance(entry, dict)\n else Structure(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n elif isinstance(optimade_response, StructureResponseOne):\n optimade_resources = [\n Structure(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Structure(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n else:\n LOGGER.debug(\n \"Could not parse response as errors, references or structures. \"\n \"Response:\\n%r\",\n optimade_response,\n )\n raise OPTIMADEParseError(\n \"Could not retrieve errors, references or structures from response \"\n f\"from {optimade_url}. It could be a valid OPTIMADE API response, \"\n \"however it may not be supported by OTEAPI-OPTIMADE. It may also be an \"\n \"invalid response completely.\"\n )\n\n session.optimade_resources = [\n model2dict(resource) for resource in optimade_resources\n ]\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionUnion[SessionUpdate, DLiteSessionUpdate]
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"all_strategies/#oteapi_optimade.strategies.resource.use_dlite","title":"def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n) -> \"Union[SessionUpdate, DLiteSessionUpdate]\":\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n ):\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n return SessionUpdate()\n
use_dlite(access_service, use_dlite_flag)
","text":"Determine whether DLite should be utilized in the Resource strategy.
Parameters:
Name Type Description Defaultaccess_service
str
The accessService value from the resource's configuration.
requireduse_dlite_flag
bool
The strategy-specific
requireduse_dlite
configuration option.Returns:
Type Descriptionbool
Based on the accessService value, then whether DLite should be used or not.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"api_reference/exceptions/","title":"exceptions","text":"def use_dlite(access_service: str, use_dlite_flag: bool) -> bool:\n \"\"\"Determine whether DLite should be utilized in the Resource strategy.\n\n Parameters:\n access_service: The accessService value from the resource's configuration.\n use_dlite_flag: The strategy-specific `use_dlite` configuration option.\n\n Returns:\n Based on the accessService value, then whether DLite should be used or not.\n\n \"\"\"\n if (\n any(dlite_form in access_service for dlite_form in [\"DLite\", \"dlite\"])\n or use_dlite_flag\n ):\n if oteapi_dlite_version is None:\n raise MissingDependency(\n \"OTEAPI-DLite is not found on the system. This is required to use \"\n \"DLite with the OTEAPI-OPTIMADE strategies.\"\n )\n return True\n return False\n
OTE-API OPTIMADE-specific Python exceptions.
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.BaseOteapiOptimadeException","title":"BaseOteapiOptimadeException (Exception)
","text":"Base OTE-API OPTIMADE exception.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.ConfigurationError","title":"class BaseOteapiOptimadeException(Exception):\n \"\"\"Base OTE-API OPTIMADE exception.\"\"\"\n
ConfigurationError (BaseOteapiOptimadeException)
","text":"An error occurred when dealing with strategy configurations.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.MissingDependency","title":"class ConfigurationError(BaseOteapiOptimadeException):\n \"\"\"An error occurred when dealing with strategy configurations.\"\"\"\n
MissingDependency (BaseOteapiOptimadeException)
","text":"A required dependency is missing.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.OPTIMADEParseError","title":"class MissingDependency(BaseOteapiOptimadeException):\n \"\"\"A required dependency is missing.\"\"\"\n
OPTIMADEParseError (BaseOteapiOptimadeException)
","text":"Could not use OPTIMADE Python tools to parse an OPTIMADE API response.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.OPTIMADEResponseError","title":"class OPTIMADEParseError(BaseOteapiOptimadeException):\n \"\"\"Could not use OPTIMADE Python tools to parse an OPTIMADE API response.\"\"\"\n
OPTIMADEResponseError (RequestError)
","text":"An OPTIMADE error was returned from a URL request.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/exceptions/#oteapi_optimade.exceptions.RequestError","title":"class OPTIMADEResponseError(RequestError):\n \"\"\"An OPTIMADE error was returned from a URL request.\"\"\"\n
RequestError (BaseOteapiOptimadeException)
","text":"A general error occured when performing a URL request.
Source code inoteapi_optimade/exceptions.py
"},{"location":"api_reference/utils/","title":"utils","text":"class RequestError(BaseOteapiOptimadeException):\n \"\"\"A general error occured when performing a URL request.\"\"\"\n
Utility functions to be used in OTEAPI OPTIMADE.
"},{"location":"api_reference/utils/#oteapi_optimade.utils.model2dict","title":"model2dict(model, **dict_kwargs)
","text":"Convert a pydantic model to a Python dictionary.
This works similarly to the
dict()
method for pydantic models, but ensures any and all nested pydantic models are also converted to dictionaries.Parameters:
Name Type Description Defaultmodel
Union[BaseModel, dict[str, Any]]
The pydantic model or Python dictionary to be converted fully to a Python dictionary, through and through.
required**dict_kwargs
Dict[Any, Any]
Keyword arguments to be passed to
dict()
method calls for pydantic models. Note, this will be used for alldict()
method calls.{}
Returns:
Type Descriptiondict[str, Any]
A Python dictionary, where all nested values that were pydantic models are also converted to Python dictionaries.
Source code inoteapi_optimade/utils.py
"},{"location":"api_reference/dlite/parse/","title":"parse","text":"def model2dict(\n model: \"Union[BaseModel, dict[str, Any]]\", **dict_kwargs: \"Any\"\n) -> \"dict[str, Any]\":\n \"\"\"Convert a pydantic model to a Python dictionary.\n\n This works similarly to the `dict()` method for pydantic models, but ensures any\n and all nested pydantic models are also converted to dictionaries.\n\n Parameters:\n model: The pydantic model or Python dictionary to be converted fully to a\n Python dictionary, through and through.\n **dict_kwargs (Dict[Any, Any]): Keyword arguments to be passed to `dict()`\n method calls for pydantic models.\n Note, this will be used for _all_ `dict()` method calls.\n\n Returns:\n A Python dictionary, where all nested values that were pydantic models are also\n converted to Python dictionaries.\n\n \"\"\"\n\n def _internal(model_: \"Any\") -> \"Any\":\n \"\"\"Internal function to be used recursively.\"\"\"\n if isinstance(model_, dict):\n return {key: _internal(value) for key, value in model_.items()}\n if isinstance(model_, Iterable) and not isinstance(model_, (bytes, str)):\n return type(model_)(_internal(value) for value in model_) # type: ignore[call-arg]\n if isinstance(model_, BaseModel):\n return _internal(model_.dict(**dict_kwargs))\n return model_\n\n if isinstance(model, BaseModel):\n res = model.dict(**dict_kwargs)\n elif isinstance(model, dict):\n res = deepcopy(model)\n else:\n raise TypeError(\"model must be either a pydantic model or a dict.\")\n\n return _internal(res)\n
OTEAPI strategy for parsing OPTIMADE structure resources to DLite instances.
"},{"location":"api_reference/dlite/parse/#oteapi_optimade.dlite.parse.OPTIMADEDLiteParseStrategy","title":"OPTIMADEDLiteParseStrategy
dataclass
","text":"Parse strategy for JSON.
Implements strategies:
Source code in
(\"mediaType\", \"application/vnd.optimade+dlite\")
(\"mediaType\", \"application/vnd.OPTIMADE+dlite\")
(\"mediaType\", \"application/vnd.OPTiMaDe+dlite\")
(\"mediaType\", \"application/vnd.optimade+DLite\")
(\"mediaType\", \"application/vnd.OPTIMADE+DLite\")
(\"mediaType\", \"application/vnd.OPTiMaDe+DLite\")
oteapi_optimade/dlite/parse.py
"},{"location":"api_reference/dlite/parse/#oteapi_optimade.dlite.parse.OPTIMADEDLiteParseStrategy.get","title":"@dataclass\nclass OPTIMADEDLiteParseStrategy:\n \"\"\"Parse strategy for JSON.\n\n **Implements strategies**:\n\n - `(\"mediaType\", \"application/vnd.optimade+dlite\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+dlite\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+dlite\")`\n - `(\"mediaType\", \"application/vnd.optimade+DLite\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+DLite\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+DLite\")`\n\n \"\"\"\n\n parse_config: OPTIMADEDLiteParseConfig\n\n def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n ) -> DLiteSessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n ---\n\n The OPTIMADE Structure needs to be parsed into DLite instances inside-out,\n meaning the most nested data structures must first be parsed, and then the ones\n 1 layer up and so on until the most upper layer can be parsed.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n session = OPTIMADEParseStrategy(self.parse_config).get(session)\n\n entities_path = Path(__file__).resolve().parent.resolve() / \"entities\"\n\n dlite.storage_path.append(str(entities_path / \"*.yaml\"))\n\n # JSONAPIResourceLinks = dlite.Instance.from_url(\n # f\"yaml://{entities_path}/JSONAPIResourceLinks.yaml\"\n # )\n OPTIMADEStructure = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructure.yaml\"\n )\n OPTIMADEStructureAssembly = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureAssembly.yaml\"\n )\n OPTIMADEStructureAttributes = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureAttributes.yaml\"\n )\n OPTIMADEStructureSpecies = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureSpecies.yaml\"\n )\n\n if self.parse_config.configuration.return_object:\n # The response is given as a \"proper\" pydantic data model instance\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the \"\n \"session.\"\n )\n\n # Currently, only \"structures\" entries are supported and handled\n if isinstance(session.optimade_response_object, StructureResponseMany):\n structures = [\n Structure(entry)\n if isinstance(entry, dict)\n else Structure(entry.dict())\n for entry in session.optimade_response_object.data\n ]\n elif isinstance(session.optimade_response_object, StructureResponseOne):\n structures = [\n Structure(session.optimade_response_object.data)\n if isinstance(session.optimade_response_object.data, dict)\n else Structure(session.optimade_response_object.data.dict())\n ]\n elif isinstance(session.optimade_response_object, Success):\n if isinstance(session.optimade_response_object.data, dict):\n structures = [Structure(session.optimade_response_object.data)]\n elif isinstance(session.optimade_response_object.data, BaseModel):\n structures = [\n Structure(session.optimade_response_object.data.dict())\n ]\n elif isinstance(session.optimade_response_object.data, list):\n structures = [\n Structure(entry)\n if isinstance(entry, dict)\n else Structure(entry.dict())\n for entry in session.optimade_response_object.data\n ]\n else:\n LOGGER.debug(\n \"Could not determine what to do with `data`. Type %s.\",\n type(session.optimade_response_object.data),\n )\n raise OPTIMADEParseError(\n \"Could not parse `data` entry in response.\"\n )\n else:\n LOGGER.debug(\n \"Got currently unsupported response type %s. Only structures are \"\n \"supported.\",\n session.optimade_response_object.__class__.__name__,\n )\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n )\n else:\n # The response is given as pure Python dictionary\n\n if \"optimade_response\" not in session:\n raise ValueError(\n \"'optimade_response' was expected to be present in the session.\"\n )\n\n if not session.optimade_response or \"data\" not in session.optimade_response:\n LOGGER.debug(\"Not a successful response - no 'data' entry found.\")\n return session\n\n if isinstance(session.optimade_response[\"data\"], list):\n try:\n structures = [\n Structure(entry) for entry in session.optimade_response[\"data\"]\n ]\n except ValidationError as exc:\n LOGGER.debug(\n \"Could not parse list of 'data' entries as structures.\"\n )\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n ) from exc\n elif session.optimade_response is not None:\n try:\n structures = [Structure(session.optimade_response[\"data\"])]\n except ValidationError as exc:\n LOGGER.debug(\"Could not parse single 'data' entry as a structure.\")\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n ) from exc\n else:\n LOGGER.debug(\"Could not parse 'data' entries as structures.\")\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n )\n\n dlite_collection = get_collection(session)\n\n # DLite-fy OPTIMADE structures\n for structure in structures:\n new_structure_attributes: dict[str, \"Any\"] = {}\n\n # Most inner layer: assemblies & species\n if structure.attributes.assemblies:\n # Non-zero length list of assemblies (which could be a list of dicts or\n # a list of pydantic models)\n\n new_structure_attributes[\"assemblies\"] = []\n\n for assembly in structure.attributes.assemblies:\n # Ensure we're dealing with a normal Python dict\n assembly = (\n assembly.dict(exclude_none=True)\n if isinstance(assembly, BaseModel)\n else assembly\n )\n\n dimensions = {\n \"ngroups\": len(assembly.get(\"group_probabilities\", []) or []),\n \"nsites\": len(assembly.get(\"sites_in_groups\", []) or []),\n }\n new_structure_attributes[\"assemblies\"].append(\n OPTIMADEStructureAssembly(\n dimensions=dimensions, properties=assembly\n )\n )\n\n if structure.attributes.species:\n # Non-zero length list of species (which could be a list of dicts or a\n # list of pydantic models)\n\n new_structure_attributes[\"species\"] = []\n\n for species_individual in structure.attributes.species:\n # Ensure we're dealing with a normal Python dict\n species_individual = (\n species_individual.dict(exclude_none=True)\n if isinstance(species_individual, BaseModel)\n else species_individual\n )\n\n dimensions = {\n \"nelements\": len(\n species_individual.get(\"chemical_symbols\", []) or []\n ),\n \"nattached_elements\": len(\n species_individual.get(\"attached\", []) or []\n ),\n }\n new_structure_attributes[\"species\"].append(\n OPTIMADEStructureSpecies(\n dimensions=dimensions,\n properties=species_individual,\n )\n )\n\n # Attributes\n new_structure_attributes.update(\n structure.attributes.dict(\n exclude={\"species\", \"assemblies\", \"nelements\", \"nsites\"}\n )\n )\n for key in list(new_structure_attributes):\n if key.startswith(\"_\"):\n new_structure_attributes.pop(key)\n\n # Structure features values are Enum values, so we need to convert them to\n # their string (true) values\n new_structure_attributes[\"structure_features\"] = [\n _.value for _ in new_structure_attributes[\"structure_features\"]\n ]\n\n new_structure = OPTIMADEStructure(\n dimensions={},\n properties={\n \"attributes\": OPTIMADEStructureAttributes(\n dimensions={\n \"nelements\": structure.attributes.nelements or 0,\n \"dimensionality\": 3,\n \"nsites\": structure.attributes.nsites or 0,\n \"nspecies\": len(structure.attributes.species)\n if structure.attributes.species\n else 0,\n \"nstructure_features\": len(\n structure.attributes.structure_features\n ),\n },\n properties=new_structure_attributes,\n ),\n \"type\": structure.entry.type,\n \"id\": structure.entry.id,\n },\n )\n dlite_collection.add(label=structure.entry.id, inst=new_structure)\n\n update_collection(collection=dlite_collection)\n\n return session\n
get(self, session=None)
","text":"Request and parse an OPTIMADE response using OPT.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromdownloadUrl
.Workflow:
- Request OPTIMADE response.
- Parse as an OPTIMADE Python tools (OPT) pydantic response model.
The OPTIMADE Structure needs to be parsed into DLite instances inside-out, meaning the most nested data structures must first be parsed, and then the ones 1 layer up and so on until the most upper layer can be parsed.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEParseSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/dlite/parse.py
"},{"location":"api_reference/dlite/parse/#oteapi_optimade.dlite.parse.OPTIMADEDLiteParseStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n ---\n\n The OPTIMADE Structure needs to be parsed into DLite instances inside-out,\n meaning the most nested data structures must first be parsed, and then the ones\n 1 layer up and so on until the most upper layer can be parsed.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n session = OPTIMADEParseStrategy(self.parse_config).get(session)\n\n entities_path = Path(__file__).resolve().parent.resolve() / \"entities\"\n\n dlite.storage_path.append(str(entities_path / \"*.yaml\"))\n\n # JSONAPIResourceLinks = dlite.Instance.from_url(\n # f\"yaml://{entities_path}/JSONAPIResourceLinks.yaml\"\n # )\n OPTIMADEStructure = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructure.yaml\"\n )\n OPTIMADEStructureAssembly = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureAssembly.yaml\"\n )\n OPTIMADEStructureAttributes = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureAttributes.yaml\"\n )\n OPTIMADEStructureSpecies = dlite.Instance.from_url(\n f\"yaml://{entities_path}/OPTIMADEStructureSpecies.yaml\"\n )\n\n if self.parse_config.configuration.return_object:\n # The response is given as a \"proper\" pydantic data model instance\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the \"\n \"session.\"\n )\n\n # Currently, only \"structures\" entries are supported and handled\n if isinstance(session.optimade_response_object, StructureResponseMany):\n structures = [\n Structure(entry)\n if isinstance(entry, dict)\n else Structure(entry.dict())\n for entry in session.optimade_response_object.data\n ]\n elif isinstance(session.optimade_response_object, StructureResponseOne):\n structures = [\n Structure(session.optimade_response_object.data)\n if isinstance(session.optimade_response_object.data, dict)\n else Structure(session.optimade_response_object.data.dict())\n ]\n elif isinstance(session.optimade_response_object, Success):\n if isinstance(session.optimade_response_object.data, dict):\n structures = [Structure(session.optimade_response_object.data)]\n elif isinstance(session.optimade_response_object.data, BaseModel):\n structures = [\n Structure(session.optimade_response_object.data.dict())\n ]\n elif isinstance(session.optimade_response_object.data, list):\n structures = [\n Structure(entry)\n if isinstance(entry, dict)\n else Structure(entry.dict())\n for entry in session.optimade_response_object.data\n ]\n else:\n LOGGER.debug(\n \"Could not determine what to do with `data`. Type %s.\",\n type(session.optimade_response_object.data),\n )\n raise OPTIMADEParseError(\n \"Could not parse `data` entry in response.\"\n )\n else:\n LOGGER.debug(\n \"Got currently unsupported response type %s. Only structures are \"\n \"supported.\",\n session.optimade_response_object.__class__.__name__,\n )\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n )\n else:\n # The response is given as pure Python dictionary\n\n if \"optimade_response\" not in session:\n raise ValueError(\n \"'optimade_response' was expected to be present in the session.\"\n )\n\n if not session.optimade_response or \"data\" not in session.optimade_response:\n LOGGER.debug(\"Not a successful response - no 'data' entry found.\")\n return session\n\n if isinstance(session.optimade_response[\"data\"], list):\n try:\n structures = [\n Structure(entry) for entry in session.optimade_response[\"data\"]\n ]\n except ValidationError as exc:\n LOGGER.debug(\n \"Could not parse list of 'data' entries as structures.\"\n )\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n ) from exc\n elif session.optimade_response is not None:\n try:\n structures = [Structure(session.optimade_response[\"data\"])]\n except ValidationError as exc:\n LOGGER.debug(\"Could not parse single 'data' entry as a structure.\")\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n ) from exc\n else:\n LOGGER.debug(\"Could not parse 'data' entries as structures.\")\n raise OPTIMADEParseError(\n \"The DLite OPTIMADE Parser currently only supports structures \"\n \"entities.\"\n )\n\n dlite_collection = get_collection(session)\n\n # DLite-fy OPTIMADE structures\n for structure in structures:\n new_structure_attributes: dict[str, \"Any\"] = {}\n\n # Most inner layer: assemblies & species\n if structure.attributes.assemblies:\n # Non-zero length list of assemblies (which could be a list of dicts or\n # a list of pydantic models)\n\n new_structure_attributes[\"assemblies\"] = []\n\n for assembly in structure.attributes.assemblies:\n # Ensure we're dealing with a normal Python dict\n assembly = (\n assembly.dict(exclude_none=True)\n if isinstance(assembly, BaseModel)\n else assembly\n )\n\n dimensions = {\n \"ngroups\": len(assembly.get(\"group_probabilities\", []) or []),\n \"nsites\": len(assembly.get(\"sites_in_groups\", []) or []),\n }\n new_structure_attributes[\"assemblies\"].append(\n OPTIMADEStructureAssembly(\n dimensions=dimensions, properties=assembly\n )\n )\n\n if structure.attributes.species:\n # Non-zero length list of species (which could be a list of dicts or a\n # list of pydantic models)\n\n new_structure_attributes[\"species\"] = []\n\n for species_individual in structure.attributes.species:\n # Ensure we're dealing with a normal Python dict\n species_individual = (\n species_individual.dict(exclude_none=True)\n if isinstance(species_individual, BaseModel)\n else species_individual\n )\n\n dimensions = {\n \"nelements\": len(\n species_individual.get(\"chemical_symbols\", []) or []\n ),\n \"nattached_elements\": len(\n species_individual.get(\"attached\", []) or []\n ),\n }\n new_structure_attributes[\"species\"].append(\n OPTIMADEStructureSpecies(\n dimensions=dimensions,\n properties=species_individual,\n )\n )\n\n # Attributes\n new_structure_attributes.update(\n structure.attributes.dict(\n exclude={\"species\", \"assemblies\", \"nelements\", \"nsites\"}\n )\n )\n for key in list(new_structure_attributes):\n if key.startswith(\"_\"):\n new_structure_attributes.pop(key)\n\n # Structure features values are Enum values, so we need to convert them to\n # their string (true) values\n new_structure_attributes[\"structure_features\"] = [\n _.value for _ in new_structure_attributes[\"structure_features\"]\n ]\n\n new_structure = OPTIMADEStructure(\n dimensions={},\n properties={\n \"attributes\": OPTIMADEStructureAttributes(\n dimensions={\n \"nelements\": structure.attributes.nelements or 0,\n \"dimensionality\": 3,\n \"nsites\": structure.attributes.nsites or 0,\n \"nspecies\": len(structure.attributes.species)\n if structure.attributes.species\n else 0,\n \"nstructure_features\": len(\n structure.attributes.structure_features\n ),\n },\n properties=new_structure_attributes,\n ),\n \"type\": structure.entry.type,\n \"id\": structure.entry.id,\n },\n )\n dlite_collection.add(label=structure.entry.id, inst=new_structure)\n\n update_collection(collection=dlite_collection)\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionDLiteSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/dlite/parse.py
"},{"location":"api_reference/models/config/","title":"config","text":"def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n) -> DLiteSessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n
General OPTIMADE configuration models.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.DEFAULT_CACHE_CONFIG_VALUES","title":"DEFAULT_CACHE_CONFIG_VALUES
","text":"Set the
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig","title":"expireTime
andtag
to default values for the data cache.OPTIMADEConfig (AttrDict)
pydantic-model
","text":"OPTIMADE configuration.
Source code inoteapi_optimade/models/config.py
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.datacache_config","title":"class OPTIMADEConfig(AttrDict):\n \"\"\"OPTIMADE configuration.\"\"\"\n\n version: str = Field(\n \"v1\",\n description=\"The version part of the OPTIMADE versioned base URL.\",\n regex=r\"^v[0-9]+(\\.[0-9]+){,2}$\",\n )\n endpoint: Literal[\"references\", \"structures\"] = Field(\n \"structures\",\n description=\"Supported OPTIMADE entry resource endpoint.\",\n )\n query_parameters: Optional[OPTIMADEQueryParameters] = Field(\n None,\n description=\"URL query parameters to be used in the OPTIMADE query.\",\n )\n datacache_config: DataCacheConfig = Field(\n DataCacheConfig(**DEFAULT_CACHE_CONFIG_VALUES),\n description=\"Configuration options for the local data cache.\",\n )\n return_object: bool = Field(\n False,\n description=(\n \"Whether or not to return a response object (using the pydantic model).\\n\"\n \"\\nImportant:\\n This should _only_ be used if the strategy is called \"\n \"directly and not via an OTEAPI REST API service.\"\n ),\n )\n use_dlite: bool = Field(\n False,\n description=\"Whether or not to store the results in a DLite Collection.\",\n )\n\n @validator(\"datacache_config\")\n def default_datacache_config(cls, value: DataCacheConfig) -> DataCacheConfig:\n \"\"\"Use default values for `DataCacheConfig` if not supplied.\"\"\"\n original_set_values = len(value.__fields_set__)\n\n for field, default_value in DEFAULT_CACHE_CONFIG_VALUES.items():\n if field in value.__fields_set__:\n # Use the set value instead of the default\n continue\n setattr(value, field, default_value)\n\n if len(value.__fields_set__) > original_set_values:\n # Re-validate model and return it\n return value.validate(\n {\n field: field_value\n for field, field_value in value.dict().items()\n if field in value.__fields_set__\n }\n )\n return value\n
datacache_config: DataCacheConfig
pydantic-field
","text":"Configuration options for the local data cache.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.endpoint","title":"endpoint: Literal['references', 'structures']
pydantic-field
","text":"Supported OPTIMADE entry resource endpoint.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.query_parameters","title":"query_parameters: OPTIMADEQueryParameters
pydantic-field
","text":"URL query parameters to be used in the OPTIMADE query.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.return_object","title":"return_object: bool
pydantic-field
","text":"Whether or not to return a response object (using the pydantic model).
Important
This should only be used if the strategy is called directly and not via an OTEAPI REST API service.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.use_dlite","title":"use_dlite: bool
pydantic-field
","text":"Whether or not to store the results in a DLite Collection.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.version","title":"version: ConstrainedStrValue
pydantic-field
","text":"The version part of the OPTIMADE versioned base URL.
"},{"location":"api_reference/models/config/#oteapi_optimade.models.config.OPTIMADEConfig.default_datacache_config","title":"default_datacache_config(value)
classmethod
","text":"Use default values for
Source code inDataCacheConfig
if not supplied.oteapi_optimade/models/config.py
"},{"location":"api_reference/models/custom_types/","title":"custom_types","text":"@validator(\"datacache_config\")\ndef default_datacache_config(cls, value: DataCacheConfig) -> DataCacheConfig:\n \"\"\"Use default values for `DataCacheConfig` if not supplied.\"\"\"\n original_set_values = len(value.__fields_set__)\n\n for field, default_value in DEFAULT_CACHE_CONFIG_VALUES.items():\n if field in value.__fields_set__:\n # Use the set value instead of the default\n continue\n setattr(value, field, default_value)\n\n if len(value.__fields_set__) > original_set_values:\n # Re-validate model and return it\n return value.validate(\n {\n field: field_value\n for field, field_value in value.dict().items()\n if field in value.__fields_set__\n }\n )\n return value\n
Custom \"pydantic\" types used in OTEAPI-OPTIMADE.
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.LOGGER","title":"LOGGER
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl","title":"OPTIMADEUrl (str)
","text":"A deconstructed OPTIMADE URL.
An OPTIMADE URL is made up in the following way:
<BASE URL>/[<VERSION>/]<ENDPOINT>?<QUERY PARAMETERS>\n
Where parts in square brackets (
Source code in[]
) are optional.oteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.allowed_schemes","title":"class OPTIMADEUrl(str):\n \"\"\"A deconstructed OPTIMADE URL.\n\n An OPTIMADE URL is made up in the following way:\n\n <BASE URL>/[<VERSION>/]<ENDPOINT>?<QUERY PARAMETERS>\n\n Where parts in square brackets (`[]`) are optional.\n \"\"\"\n\n strip_whitespace = True\n min_length = 1\n # https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers\n max_length = 2083\n allowed_schemes = {\"http\", \"https\"}\n tld_required = False\n user_required = False\n\n __slots__ = (\n \"base_url\",\n \"version\",\n \"endpoint\",\n \"query\",\n \"scheme\",\n \"tld\",\n \"host_type\",\n )\n\n @no_type_check\n def __new__(cls, url: \"Optional[str]\" = None, **kwargs) -> object:\n return str.__new__(\n cls,\n cls.build(**kwargs) if url is None else url,\n )\n\n def __init__(\n self,\n url: str,\n *,\n base_url: \"Optional[str]\" = None,\n version: \"Optional[str]\" = None,\n endpoint: \"Optional[str]\" = None,\n query: \"Optional[str]\" = None,\n scheme: \"Optional[str]\" = None,\n tld: \"Optional[str]\" = None,\n host_type: str = \"domain\",\n ) -> None:\n str.__init__(url)\n self.base_url = base_url\n self.version = version\n self.endpoint = endpoint\n self.query = query\n self.scheme = scheme\n self.tld = tld\n self.host_type = host_type\n\n @classmethod\n def build(\n cls,\n *,\n base_url: \"str\",\n version: \"Optional[str]\" = None,\n endpoint: \"Optional[str]\" = None,\n query: \"Optional[str]\" = None,\n **_kwargs: str,\n ) -> str:\n \"\"\"Build complete URL from URL parts.\"\"\"\n url = base_url.rstrip(\"/\")\n if version:\n url += f\"/{version}\"\n if endpoint:\n url += f\"/{endpoint}\"\n if query:\n url += f\"?{query}\"\n return url\n\n @classmethod\n def __modify_schema__(cls, field_schema: \"Dict[str, Any]\") -> None:\n update_not_none(\n field_schema,\n minLength=cls.min_length,\n maxLength=cls.max_length,\n format=\"uri\",\n )\n\n @classmethod\n def __get_validators__(cls) -> \"CallableGenerator\":\n yield cls.validate\n\n @staticmethod\n def urlquote_qs(url: str) -> str:\n \"\"\"Use `urllib.parse.quote` for query part of URL.\"\"\"\n parsed_url = urlparse(url)\n quoted_query = urlquote(parsed_url.query, safe=\"=&,\")\n parsed_url_list = list(parsed_url)\n parsed_url_list[-2] = quoted_query\n return urlunparse(parsed_url_list)\n\n @classmethod\n def validate(\n cls, value: \"Any\", field: \"ModelField\", config: \"BaseConfig\"\n ) -> \"OPTIMADEUrl\":\n \"\"\"Pydantic validation of an OPTIMADE URL.\"\"\"\n if value.__class__ == cls:\n return value\n\n value: str = str_validator(value)\n if cls.strip_whitespace:\n value = value.strip()\n url: str = cast(str, constr_length_validator(value, field, config))\n url = cls.urlquote_qs(url)\n\n url_match = url_regex().match(url)\n if url_match is None:\n raise ValueError(f\"Cannot match URL ({url!r}) as a valid URL.\")\n\n original_parts = cast(\"Parts\", url_match.groupdict())\n parts = cls.apply_default_parts(original_parts)\n host, tld, host_type, rebuild = cls.validate_host(parts)\n optimade_parts = cls.build_optimade_parts(parts, host)\n optimade_parts = cls.validate_parts(parts, optimade_parts)\n\n if url_match.end() != len(url):\n raise errors.UrlExtraError(extra=url[url_match.end() :])\n\n return cls(\n None if rebuild else url,\n base_url=optimade_parts[\"base_url\"],\n version=optimade_parts[\"version\"],\n endpoint=optimade_parts[\"endpoint\"],\n query=optimade_parts[\"query\"],\n scheme=parts[\"scheme\"],\n tld=tld,\n host_type=host_type,\n )\n\n @classmethod\n def validate_host(cls, parts: \"Parts\") -> \"Tuple[str, Optional[str], str, bool]\":\n \"\"\"Validate host-part of the URL.\"\"\"\n host: \"Optional[str]\" = None\n tld: \"Optional[str]\" = None\n rebuild: bool = False\n for host_type in (\"domain\", \"ipv4\", \"ipv6\"):\n host = parts[host_type] # type: ignore[literal-required]\n if host:\n break\n else:\n raise errors.UrlHostError()\n\n if host_type == \"domain\":\n is_international = False\n domain = ascii_domain_regex().fullmatch(host)\n if domain is None:\n domain = int_domain_regex().fullmatch(host)\n if domain is None:\n raise errors.UrlHostError()\n is_international = True\n\n tld = domain.group(\"tld\")\n if tld is None and not is_international:\n domain = int_domain_regex().fullmatch(host)\n if domain is None:\n raise ValueError(\"domain cannot be None\")\n tld = domain.group(\"tld\")\n is_international = True\n\n if tld is not None:\n tld = tld[1:]\n elif cls.tld_required:\n raise errors.UrlHostTldError()\n\n if is_international:\n host_type = \"int_domain\"\n rebuild = True\n host = host.encode(\"idna\").decode(\"ascii\")\n if tld is not None:\n tld = tld.encode(\"idna\").decode(\"ascii\")\n\n return host, tld, host_type, rebuild\n\n @staticmethod\n def get_default_parts(parts: \"Parts\") -> \"Parts\":\n \"\"\"Dictionary of default URL-part values.\"\"\"\n return {\"port\": \"80\" if parts[\"scheme\"] == \"http\" else \"443\"}\n\n @classmethod\n def apply_default_parts(cls, parts: \"Parts\") -> \"Parts\":\n \"\"\"Apply default URL-part values if no value is given.\"\"\"\n for key, value in cls.get_default_parts(parts).items():\n if not parts[key]: # type: ignore[literal-required]\n parts[key] = value # type: ignore[literal-required]\n return parts\n\n @classmethod\n def build_optimade_parts(cls, parts: \"Parts\", host: str) -> \"OPTIMADEParts\":\n \"\"\"Convert URL parts to equivalent OPTIMADE URL parts.\"\"\"\n base_url = f\"{parts['scheme']}://\"\n if parts[\"user\"]:\n base_url += parts[\"user\"]\n if parts[\"password\"]:\n base_url += f\":{parts['password']}\"\n if parts[\"user\"] or parts[\"password\"]:\n base_url += \"@\"\n base_url += host\n # Hide port if it's a standard HTTP (80) or HTTPS (443) port.\n if parts[\"port\"] and parts[\"port\"] not in (\"80\", \"443\"):\n base_url += f\":{parts['port']}\"\n if parts[\"path\"]:\n base_url += parts[\"path\"]\n\n base_url_match = optimade_base_url_regex().fullmatch(base_url)\n LOGGER.debug(\n \"OPTIMADE base URL regex match groups: %s\",\n base_url_match.groupdict() if base_url_match else base_url_match,\n )\n if base_url_match is None:\n raise ValueError(\n \"Could not match given string with OPTIMADE base URL regex.\"\n )\n\n endpoint_match = optimade_endpoint_regex().findall(\n base_url_match.group(\"path\") if base_url_match.group(\"path\") else \"\"\n )\n LOGGER.debug(\"OPTIMADE endpoint regex matches: %s\", endpoint_match)\n for path_version, path_endpoint in endpoint_match:\n if path_endpoint:\n break\n else:\n LOGGER.debug(\"Could not match given string with OPTIMADE endpoint regex.\")\n path_version, path_endpoint = \"\", \"\"\n\n base_url = base_url_match.group(\"base_url\")\n if path_version:\n base_url = base_url[: -(len(path_version) + len(path_endpoint) + 2)]\n elif path_endpoint:\n base_url = base_url[: -(len(path_endpoint) + 1)]\n\n optimade_parts = {\n \"base_url\": base_url.rstrip(\"/\"),\n \"version\": path_version or None,\n \"endpoint\": path_endpoint or None,\n \"query\": parts[\"query\"],\n }\n return cast(\"OPTIMADEParts\", optimade_parts)\n\n @classmethod\n def validate_parts(\n cls, parts: \"Parts\", optimade_parts: \"OPTIMADEParts\"\n ) -> \"OPTIMADEParts\":\n \"\"\"\n A method used to validate parts of an URL.\n Could be overridden to set default values for parts if missing\n \"\"\"\n scheme = parts[\"scheme\"]\n if scheme is None:\n raise errors.UrlSchemeError()\n\n if cls.allowed_schemes and scheme.lower() not in cls.allowed_schemes:\n raise errors.UrlSchemePermittedError(set(cls.allowed_schemes))\n\n port = parts[\"port\"]\n if port is not None and int(port) > 65_535:\n raise errors.UrlPortError()\n\n user = parts[\"user\"]\n if cls.user_required and user is None:\n raise errors.UrlUserInfoError()\n\n base_url = optimade_parts[\"base_url\"]\n if base_url is None:\n raise errors.UrlError()\n\n return optimade_parts\n\n def __repr__(self) -> str:\n extra = \", \".join(\n f\"{n}={getattr(self, n)!r}\"\n for n in self.__slots__\n if getattr(self, n) is not None\n )\n return f\"{self.__class__.__name__}({super().__repr__()}, {extra})\"\n\n def response_model(self) -> \"Union[Tuple[Success, ...], Success, None]\":\n \"\"\"Return the endpoint's corresponding response model (from OPT).\"\"\"\n if not self.endpoint or self.endpoint == \"versions\":\n return None\n return {\n \"info\": (InfoResponse, EntryInfoResponse),\n \"links\": LinksResponse,\n \"structures\": (StructureResponseMany, StructureResponseOne),\n \"references\": (ReferenceResponseMany, ReferenceResponseOne),\n \"calculations\": (EntryResponseMany, EntryResponseOne),\n }.get(self.endpoint, Success)\n
allowed_schemes
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.max_length","title":"max_length
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.min_length","title":"min_length
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.strip_whitespace","title":"strip_whitespace
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.tld_required","title":"tld_required
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.user_required","title":"user_required
","text":""},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.__init__","title":"__init__(self, url, *, base_url=None, version=None, endpoint=None, query=None, scheme=None, tld=None, host_type='domain')
special
","text":"Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.apply_default_parts","title":"def __init__(\n self,\n url: str,\n *,\n base_url: \"Optional[str]\" = None,\n version: \"Optional[str]\" = None,\n endpoint: \"Optional[str]\" = None,\n query: \"Optional[str]\" = None,\n scheme: \"Optional[str]\" = None,\n tld: \"Optional[str]\" = None,\n host_type: str = \"domain\",\n) -> None:\n str.__init__(url)\n self.base_url = base_url\n self.version = version\n self.endpoint = endpoint\n self.query = query\n self.scheme = scheme\n self.tld = tld\n self.host_type = host_type\n
apply_default_parts(parts)
classmethod
","text":"Apply default URL-part values if no value is given.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.build","title":"@classmethod\ndef apply_default_parts(cls, parts: \"Parts\") -> \"Parts\":\n \"\"\"Apply default URL-part values if no value is given.\"\"\"\n for key, value in cls.get_default_parts(parts).items():\n if not parts[key]: # type: ignore[literal-required]\n parts[key] = value # type: ignore[literal-required]\n return parts\n
build(*, base_url, version=None, endpoint=None, query=None, **_kwargs)
classmethod
","text":"Build complete URL from URL parts.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.build_optimade_parts","title":"@classmethod\ndef build(\n cls,\n *,\n base_url: \"str\",\n version: \"Optional[str]\" = None,\n endpoint: \"Optional[str]\" = None,\n query: \"Optional[str]\" = None,\n **_kwargs: str,\n) -> str:\n \"\"\"Build complete URL from URL parts.\"\"\"\n url = base_url.rstrip(\"/\")\n if version:\n url += f\"/{version}\"\n if endpoint:\n url += f\"/{endpoint}\"\n if query:\n url += f\"?{query}\"\n return url\n
build_optimade_parts(parts, host)
classmethod
","text":"Convert URL parts to equivalent OPTIMADE URL parts.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.get_default_parts","title":"@classmethod\ndef build_optimade_parts(cls, parts: \"Parts\", host: str) -> \"OPTIMADEParts\":\n \"\"\"Convert URL parts to equivalent OPTIMADE URL parts.\"\"\"\n base_url = f\"{parts['scheme']}://\"\n if parts[\"user\"]:\n base_url += parts[\"user\"]\n if parts[\"password\"]:\n base_url += f\":{parts['password']}\"\n if parts[\"user\"] or parts[\"password\"]:\n base_url += \"@\"\n base_url += host\n # Hide port if it's a standard HTTP (80) or HTTPS (443) port.\n if parts[\"port\"] and parts[\"port\"] not in (\"80\", \"443\"):\n base_url += f\":{parts['port']}\"\n if parts[\"path\"]:\n base_url += parts[\"path\"]\n\n base_url_match = optimade_base_url_regex().fullmatch(base_url)\n LOGGER.debug(\n \"OPTIMADE base URL regex match groups: %s\",\n base_url_match.groupdict() if base_url_match else base_url_match,\n )\n if base_url_match is None:\n raise ValueError(\n \"Could not match given string with OPTIMADE base URL regex.\"\n )\n\n endpoint_match = optimade_endpoint_regex().findall(\n base_url_match.group(\"path\") if base_url_match.group(\"path\") else \"\"\n )\n LOGGER.debug(\"OPTIMADE endpoint regex matches: %s\", endpoint_match)\n for path_version, path_endpoint in endpoint_match:\n if path_endpoint:\n break\n else:\n LOGGER.debug(\"Could not match given string with OPTIMADE endpoint regex.\")\n path_version, path_endpoint = \"\", \"\"\n\n base_url = base_url_match.group(\"base_url\")\n if path_version:\n base_url = base_url[: -(len(path_version) + len(path_endpoint) + 2)]\n elif path_endpoint:\n base_url = base_url[: -(len(path_endpoint) + 1)]\n\n optimade_parts = {\n \"base_url\": base_url.rstrip(\"/\"),\n \"version\": path_version or None,\n \"endpoint\": path_endpoint or None,\n \"query\": parts[\"query\"],\n }\n return cast(\"OPTIMADEParts\", optimade_parts)\n
get_default_parts(parts)
staticmethod
","text":"Dictionary of default URL-part values.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.response_model","title":"@staticmethod\ndef get_default_parts(parts: \"Parts\") -> \"Parts\":\n \"\"\"Dictionary of default URL-part values.\"\"\"\n return {\"port\": \"80\" if parts[\"scheme\"] == \"http\" else \"443\"}\n
response_model(self)
","text":"Return the endpoint's corresponding response model (from OPT).
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.urlquote_qs","title":"def response_model(self) -> \"Union[Tuple[Success, ...], Success, None]\":\n \"\"\"Return the endpoint's corresponding response model (from OPT).\"\"\"\n if not self.endpoint or self.endpoint == \"versions\":\n return None\n return {\n \"info\": (InfoResponse, EntryInfoResponse),\n \"links\": LinksResponse,\n \"structures\": (StructureResponseMany, StructureResponseOne),\n \"references\": (ReferenceResponseMany, ReferenceResponseOne),\n \"calculations\": (EntryResponseMany, EntryResponseOne),\n }.get(self.endpoint, Success)\n
urlquote_qs(url)
staticmethod
","text":"Use
Source code inurllib.parse.quote
for query part of URL.oteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.validate","title":"@staticmethod\ndef urlquote_qs(url: str) -> str:\n \"\"\"Use `urllib.parse.quote` for query part of URL.\"\"\"\n parsed_url = urlparse(url)\n quoted_query = urlquote(parsed_url.query, safe=\"=&,\")\n parsed_url_list = list(parsed_url)\n parsed_url_list[-2] = quoted_query\n return urlunparse(parsed_url_list)\n
validate(value, field, config)
classmethod
","text":"Pydantic validation of an OPTIMADE URL.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.validate_host","title":"@classmethod\ndef validate(\n cls, value: \"Any\", field: \"ModelField\", config: \"BaseConfig\"\n) -> \"OPTIMADEUrl\":\n \"\"\"Pydantic validation of an OPTIMADE URL.\"\"\"\n if value.__class__ == cls:\n return value\n\n value: str = str_validator(value)\n if cls.strip_whitespace:\n value = value.strip()\n url: str = cast(str, constr_length_validator(value, field, config))\n url = cls.urlquote_qs(url)\n\n url_match = url_regex().match(url)\n if url_match is None:\n raise ValueError(f\"Cannot match URL ({url!r}) as a valid URL.\")\n\n original_parts = cast(\"Parts\", url_match.groupdict())\n parts = cls.apply_default_parts(original_parts)\n host, tld, host_type, rebuild = cls.validate_host(parts)\n optimade_parts = cls.build_optimade_parts(parts, host)\n optimade_parts = cls.validate_parts(parts, optimade_parts)\n\n if url_match.end() != len(url):\n raise errors.UrlExtraError(extra=url[url_match.end() :])\n\n return cls(\n None if rebuild else url,\n base_url=optimade_parts[\"base_url\"],\n version=optimade_parts[\"version\"],\n endpoint=optimade_parts[\"endpoint\"],\n query=optimade_parts[\"query\"],\n scheme=parts[\"scheme\"],\n tld=tld,\n host_type=host_type,\n )\n
validate_host(parts)
classmethod
","text":"Validate host-part of the URL.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.OPTIMADEUrl.validate_parts","title":"@classmethod\ndef validate_host(cls, parts: \"Parts\") -> \"Tuple[str, Optional[str], str, bool]\":\n \"\"\"Validate host-part of the URL.\"\"\"\n host: \"Optional[str]\" = None\n tld: \"Optional[str]\" = None\n rebuild: bool = False\n for host_type in (\"domain\", \"ipv4\", \"ipv6\"):\n host = parts[host_type] # type: ignore[literal-required]\n if host:\n break\n else:\n raise errors.UrlHostError()\n\n if host_type == \"domain\":\n is_international = False\n domain = ascii_domain_regex().fullmatch(host)\n if domain is None:\n domain = int_domain_regex().fullmatch(host)\n if domain is None:\n raise errors.UrlHostError()\n is_international = True\n\n tld = domain.group(\"tld\")\n if tld is None and not is_international:\n domain = int_domain_regex().fullmatch(host)\n if domain is None:\n raise ValueError(\"domain cannot be None\")\n tld = domain.group(\"tld\")\n is_international = True\n\n if tld is not None:\n tld = tld[1:]\n elif cls.tld_required:\n raise errors.UrlHostTldError()\n\n if is_international:\n host_type = \"int_domain\"\n rebuild = True\n host = host.encode(\"idna\").decode(\"ascii\")\n if tld is not None:\n tld = tld.encode(\"idna\").decode(\"ascii\")\n\n return host, tld, host_type, rebuild\n
validate_parts(parts, optimade_parts)
classmethod
","text":"A method used to validate parts of an URL. Could be overridden to set default values for parts if missing
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.optimade_base_url_regex","title":"@classmethod\ndef validate_parts(\n cls, parts: \"Parts\", optimade_parts: \"OPTIMADEParts\"\n) -> \"OPTIMADEParts\":\n \"\"\"\n A method used to validate parts of an URL.\n Could be overridden to set default values for parts if missing\n \"\"\"\n scheme = parts[\"scheme\"]\n if scheme is None:\n raise errors.UrlSchemeError()\n\n if cls.allowed_schemes and scheme.lower() not in cls.allowed_schemes:\n raise errors.UrlSchemePermittedError(set(cls.allowed_schemes))\n\n port = parts[\"port\"]\n if port is not None and int(port) > 65_535:\n raise errors.UrlPortError()\n\n user = parts[\"user\"]\n if cls.user_required and user is None:\n raise errors.UrlUserInfoError()\n\n base_url = optimade_parts[\"base_url\"]\n if base_url is None:\n raise errors.UrlError()\n\n return optimade_parts\n
optimade_base_url_regex()
","text":"A regular expression for an OPTIMADE base URL.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/custom_types/#oteapi_optimade.models.custom_types.optimade_endpoint_regex","title":"def optimade_base_url_regex() -> \"Pattern[str]\":\n \"\"\"A regular expression for an OPTIMADE base URL.\"\"\"\n global _OPTIMADE_BASE_URL_REGEX\n if _OPTIMADE_BASE_URL_REGEX is None:\n _OPTIMADE_BASE_URL_REGEX = re.compile(\n r\"^(?P<base_url>\"\n # scheme https://tools.ietf.org/html/rfc3986#appendix-A\n r\"(?:[a-z][a-z0-9+\\-.]+://)?\"\n r\"(?:[^\\s:/]*(?::[^\\s/]*)?@)?\" # user info\n r\"(?:\"\n r\"(?:\\d{1,3}\\.){3}\\d{1,3}(?=$|[/:#?])|\" # ipv4\n r\"\\[[A-F0-9]*:[A-F0-9:]+\\](?=$|[/:#?])|\" # ipv6\n r\"[^\\s/:?#]+\" # domain, validation occurs later\n r\")?\"\n r\"(?::\\d+)?\" # port\n r\"(?P<path>/[^\\s?#]*)?\" # path\n r\")\",\n re.IGNORECASE,\n )\n return _OPTIMADE_BASE_URL_REGEX\n
optimade_endpoint_regex()
","text":"A regular expression for an OPTIMADE base URL.
Source code inoteapi_optimade/models/custom_types.py
"},{"location":"api_reference/models/query/","title":"query","text":"def optimade_endpoint_regex() -> \"Pattern[str]\":\n \"\"\"A regular expression for an OPTIMADE base URL.\"\"\"\n global _OPTIMADE_ENDPOINT_REGEX\n if _OPTIMADE_ENDPOINT_REGEX is None:\n _OPTIMADE_ENDPOINT_REGEX = re.compile(\n # version\n r\"(?:/(?P<version>v[0-9]+(?:\\.[0-9+]){0,2})\"\n r\"(?=/info|/links|/version|/structures|/references|/calculations\"\n r\"|/extensions))?\"\n # endpoint\n r\"(?:/(?P<endpoint>(?:info|links|versions|structures|references\"\n r\"|calculations|extensions)(?:/[^\\s?#]*)?))?$\"\n )\n return _OPTIMADE_ENDPOINT_REGEX\n
Data models related to OPTIMADE queries.
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.QUERY_PARAMETERS","title":"QUERY_PARAMETERS
","text":"Entry listing URL query parameters from the
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters","title":"optimade
package (EntryListingQueryParams
).OPTIMADEQueryParameters (BaseModel)
pydantic-model
","text":"Common OPTIMADE entry listing endpoint query parameters.
Source code inoteapi_optimade/models/query.py
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.api_hint","title":"class OPTIMADEQueryParameters(BaseModel, validate_assignment=True):\n \"\"\"Common OPTIMADE entry listing endpoint query parameters.\"\"\"\n\n filter: Optional[str] = Field(\n QUERY_PARAMETERS.filter.default,\n description=QUERY_PARAMETERS.filter.description,\n )\n response_format: Optional[str] = Field(\n QUERY_PARAMETERS.response_format.default,\n description=QUERY_PARAMETERS.response_format.description,\n )\n email_address: Optional[EmailStr] = Field(\n QUERY_PARAMETERS.email_address.default,\n description=QUERY_PARAMETERS.email_address.description,\n )\n response_fields: Optional[str] = Field(\n QUERY_PARAMETERS.response_fields.default,\n description=QUERY_PARAMETERS.response_fields.description,\n regex=QUERY_PARAMETERS.response_fields.regex,\n )\n sort: Optional[str] = Field(\n QUERY_PARAMETERS.sort.default,\n description=QUERY_PARAMETERS.sort.description,\n regex=QUERY_PARAMETERS.sort.regex,\n )\n page_limit: Optional[int] = Field(\n QUERY_PARAMETERS.page_limit.default,\n description=QUERY_PARAMETERS.page_limit.description,\n ge=QUERY_PARAMETERS.page_limit.ge,\n )\n page_offset: Optional[int] = Field(\n QUERY_PARAMETERS.page_offset.default,\n description=QUERY_PARAMETERS.page_offset.description,\n ge=QUERY_PARAMETERS.page_offset.ge,\n )\n page_number: Optional[int] = Field(\n QUERY_PARAMETERS.page_number.default,\n description=QUERY_PARAMETERS.page_number.description,\n ge=QUERY_PARAMETERS.page_number.ge,\n )\n page_cursor: Optional[int] = Field(\n QUERY_PARAMETERS.page_cursor.default,\n description=QUERY_PARAMETERS.page_cursor.description,\n ge=QUERY_PARAMETERS.page_cursor.ge,\n )\n page_above: Optional[int] = Field(\n QUERY_PARAMETERS.page_above.default,\n description=QUERY_PARAMETERS.page_above.description,\n ge=QUERY_PARAMETERS.page_above.ge,\n )\n page_below: Optional[int] = Field(\n QUERY_PARAMETERS.page_below.default,\n description=QUERY_PARAMETERS.page_below.description,\n ge=QUERY_PARAMETERS.page_below.ge,\n )\n include: Optional[str] = Field(\n QUERY_PARAMETERS.include.default,\n description=QUERY_PARAMETERS.include.description,\n )\n # api_hint is not yet initialized in `EntryListingQueryParams`.\n # These values are copied verbatim from `optimade==0.16.10`.\n api_hint: Optional[str] = Field(\n \"\",\n description=(\n \"If the client provides the parameter, the value SHOULD have the format \"\n \"`vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a\"\n \" minor version of the API. For example, if a client appends \"\n \"`api_hint=v1.0` to the query string, the hint provided is for major \"\n \"version 1 and minor version 0.\"\n ),\n regex=r\"(v[0-9]+(\\.[0-9]+)?)?\",\n )\n\n def generate_query_string(self) -> str:\n \"\"\"Generate a valid URL query string based on the set fields.\"\"\"\n res = {}\n for field, value in self.dict().items():\n if value or field in self.__fields_set__:\n res[field] = unquote(value) if isinstance(value, str) else value\n return urlencode(res, quote_via=quote)\n
api_hint: ConstrainedStrValue
pydantic-field
","text":"If the client provides the parameter, the value SHOULD have the format
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.email_address","title":"vMAJOR
orvMAJOR.MINOR
, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appendsapi_hint=v1.0
to the query string, the hint provided is for major version 1 and minor version 0.email_address: EmailStr
pydantic-field
","text":"An email address of the user making the request. The email SHOULD be that of a person and not an automatic system. Example:
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.filter","title":"http://example.com/v1/structures?email_address=user@example.com
filter: str
pydantic-field
","text":"A filter string, in the format described in section API Filtering Format Specification of the specification.
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.include","title":"include: str
pydantic-field
","text":"A server MAY implement the JSON API concept of returning compound documents by utilizing the
include
query parameter as specified by JSON API 1.0.All related resource objects MUST be returned as part of an array value for the top-level
included
field, see the section JSON Response Schema: Common Fields.The value of
include
MUST be a comma-separated list of \"relationship paths\", as defined in the JSON API. If relationship paths are not supported, or a server is unable to identify a relationship path a400 Bad Request
response MUST be made.The default value for
include
isreferences
. This meansreferences
entries MUST always be included under the top-level fieldincluded
as default, since a server assumes ifinclude
is not specified by a client in the request, it is still specified asinclude=references
. Note, if a client explicitly specifiesinclude
and leaves outreferences
,references
resource objects MUST NOT be included under the top-level fieldincluded
, as per the definition ofincluded
, see section JSON Response Schema: Common Fields.Note: A query with the parameter
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_above","title":"include
set to the empty string means no related resource objects are to be returned under the top-level fieldincluded
.page_above: int
pydantic-field
","text":"RECOMMENDED for use with value-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_below","title":"page_above
/page_below
andpage_limit
is RECOMMENDED. Example: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasingid
, sopage_above
value refers to anid
value):/structures?page_above=4000&page_limit=100
.page_below: int
pydantic-field
","text":"RECOMMENDED for use with value-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_cursor","title":"page_above
/page_below
andpage_limit
is RECOMMENDED.page_cursor: ConstrainedIntValue
pydantic-field
","text":"RECOMMENDED for use with cursor-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_limit","title":"page_cursor
andpage_limit
is RECOMMENDED.page_limit: ConstrainedIntValue
pydantic-field
","text":"Sets a numerical limit on the number of entries returned. See JSON API 1.0. The API implementation MUST return no more than the number specified. It MAY return fewer. The database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned). The default limit value is up to the API implementation to decide. Example:
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_number","title":"http://example.com/optimade/v1/structures?page_limit=100
page_number: int
pydantic-field
","text":"RECOMMENDED for use with page-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.page_offset","title":"page_number
andpage_limit
is RECOMMENDED. It is RECOMMENDED that the first page has number 1, i.e., thatpage_number
is 1-based. Example: Fetch page 2 of up to 50 structures per page:/structures?page_number=2&page_limit=50
.page_offset: ConstrainedIntValue
pydantic-field
","text":"RECOMMENDED for use with offset-based pagination: using
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.response_fields","title":"page_offset
andpage_limit
is RECOMMENDED. Example: Skip 50 structures and fetch up to 100:/structures?page_offset=50&page_limit=100
.response_fields: ConstrainedStrValue
pydantic-field
","text":"A comma-delimited set of fields to be provided in the output. If provided, these fields MUST be returned along with the REQUIRED fields. Other OPTIONAL fields MUST NOT be returned when this parameter is present. Example:
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.response_format","title":"http://example.com/v1/structures?response_fields=last_modified,nsites
response_format: str
pydantic-field
","text":"The output format requested (see section Response Format). Defaults to the format string 'json', which specifies the standard output format described in this specification. Example:
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.sort","title":"http://example.com/v1/structures?response_format=xml
sort: ConstrainedStrValue
pydantic-field
","text":"If supporting sortable queries, an implementation MUST use the
sort
query parameter with format as specified by JSON API 1.0.An implementation MAY support multiple sort fields for a single query. If it does, it again MUST conform to the JSON API 1.0 specification.
If an implementation supports sorting for an entry listing endpoint, then the
"},{"location":"api_reference/models/query/#oteapi_optimade.models.query.OPTIMADEQueryParameters.generate_query_string","title":"/info/<entries>
endpoint MUST include, for each field name<fieldname>
in itsdata.properties.<fieldname>
response value that can be used for sorting, the keysortable
with valuetrue
. If a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out thesortable
key or set it equal tofalse
for the specific field name. The set of field names, withsortable
equal totrue
are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification. The fieldsortable
is in addition to each property description and other OPTIONAL fields. An example is shown in the section Entry Listing Info Endpoints.generate_query_string(self)
","text":"Generate a valid URL query string based on the set fields.
Source code inoteapi_optimade/models/query.py
"},{"location":"api_reference/models/strategies/filter/","title":"filter","text":"def generate_query_string(self) -> str:\n \"\"\"Generate a valid URL query string based on the set fields.\"\"\"\n res = {}\n for field, value in self.dict().items():\n if value or field in self.__fields_set__:\n res[field] = unquote(value) if isinstance(value, str) else value\n return urlencode(res, quote_via=quote)\n
Models specific to the filter strategy.
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterConfig","title":"OPTIMADEFilterConfig (FilterConfig)
pydantic-model
","text":"OPTIMADE-specific filter strategy config.
Note
The
Source code incondition
parameter is not taken into account.oteapi_optimade/models/strategies/filter.py
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession","title":"class OPTIMADEFilterConfig(FilterConfig):\n \"\"\"OPTIMADE-specific filter strategy config.\n\n Note:\n The `condition` parameter is not taken into account.\n\n \"\"\"\n\n filterType: Literal[\"optimade\", \"OPTIMADE\", \"OPTiMaDe\"] = Field(\n ...,\n description=\"The registered strategy name for OPTIMADEFilterStrategy.\",\n )\n query: Optional[str] = Field(\n None,\n description=(\n \"The `filter` OPTIMADE query parameter value. This parameter value can \"\n \"also be provided through the [`configuration.query_parameters.filter`]\"\n \"[oteapi_optimade.models.query.OPTIMADEQueryParameters.filter] parameter. \"\n \"Note, this value takes precedence over [`configuration`][oteapi_optimade.\"\n \"models.strategies.filter.OPTIMADEFilterConfig.configuration] values.\"\n ),\n )\n limit: Optional[int] = Field(\n None,\n description=(\n \"The `page_limit` OPTIMADE query parameter value. This parameter value can\"\n \" also be provided through the [`configuration.query_parameters.\"\n \"page_limit`][oteapi_optimade.models.query.OPTIMADEQueryParameters.\"\n \"page_limit] parameter. Note, this value takes precedence over \"\n \"[`configuration`][oteapi_optimade.models.strategies.filter.\"\n \"OPTIMADEFilterConfig.configuration] values.\"\n ),\n )\n configuration: OPTIMADEConfig = Field(\n OPTIMADEConfig(),\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n
OPTIMADEFilterSession (SessionUpdate)
pydantic-model
","text":"OPTIMADE session for the filter strategy.
Source code inoteapi_optimade/models/strategies/filter.py
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession.optimade_config","title":"class OPTIMADEFilterSession(SessionUpdate):\n \"\"\"OPTIMADE session for the filter strategy.\"\"\"\n\n optimade_config: Optional[OPTIMADEConfig] = Field(\n None,\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n optimade_response_object: Optional[Response] = Field(\n None,\n description=\"An OPTIMADE Python tools (OPT) pydantic response object.\",\n )\n optimade_response: Optional[Dict[str, Any]] = Field(\n None,\n description=\"An OPTIMADE response as a Python dictionary.\",\n )\n\n class Config:\n \"\"\"Pydantic configuration for `OPTIMADEFilterSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
optimade_config: OPTIMADEConfig
pydantic-field
","text":"OPTIMADE configuration. Contains relevant information necessary to perform OPTIMADE queries.
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession.optimade_response","title":"optimade_response: Dict[str, Any]
pydantic-field
","text":"An OPTIMADE response as a Python dictionary.
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession.optimade_response_object","title":"optimade_response_object: Response
pydantic-field
","text":"An OPTIMADE Python tools (OPT) pydantic response object.
"},{"location":"api_reference/models/strategies/filter/#oteapi_optimade.models.strategies.filter.OPTIMADEFilterSession.Config","title":"Config
","text":"Pydantic configuration for
Source code inOPTIMADEFilterSession
.oteapi_optimade/models/strategies/filter.py
"},{"location":"api_reference/models/strategies/parse/","title":"parse","text":"class Config:\n \"\"\"Pydantic configuration for `OPTIMADEFilterSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
Models specific to the parse strategy.
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEDLiteParseConfig","title":"OPTIMADEDLiteParseConfig (OPTIMADEParseConfig)
pydantic-model
","text":"OPTIMADE-specific parse strategy config.
Source code inoteapi_optimade/models/strategies/parse.py
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseConfig","title":"class OPTIMADEDLiteParseConfig(OPTIMADEParseConfig):\n \"\"\"OPTIMADE-specific parse strategy config.\"\"\"\n\n mediaType: Literal[\n \"application/vnd.optimade+dlite\",\n \"application/vnd.OPTIMADE+dlite\",\n \"application/vnd.OPTiMaDe+dlite\",\n \"application/vnd.optimade+DLite\",\n \"application/vnd.OPTIMADE+DLite\",\n \"application/vnd.OPTiMaDe+DLite\",\n ] = Field( # type: ignore[assignment]\n ...,\n description=\"The registered strategy name for OPTIMADEDLiteParseStrategy.\",\n )\n
OPTIMADEParseConfig (ResourceConfig)
pydantic-model
","text":"OPTIMADE-specific parse strategy config.
Source code inoteapi_optimade/models/strategies/parse.py
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession","title":"class OPTIMADEParseConfig(ResourceConfig):\n \"\"\"OPTIMADE-specific parse strategy config.\"\"\"\n\n downloadUrl: OPTIMADEUrl = Field(\n ...,\n description=\"Either a base OPTIMADE URL or a full OPTIMADE URL.\",\n )\n mediaType: Literal[\n \"application/vnd.optimade+json\",\n \"application/vnd.OPTIMADE+json\",\n \"application/vnd.OPTiMaDe+json\",\n \"application/vnd.optimade+JSON\",\n \"application/vnd.OPTIMADE+JSON\",\n \"application/vnd.OPTiMaDe+JSON\",\n \"application/vnd.optimade\",\n \"application/vnd.OPTIMADE\",\n \"application/vnd.OPTiMaDe\",\n ] = Field(\n ...,\n description=\"The registered strategy name for OPTIMADEParseStrategy.\",\n )\n configuration: OPTIMADEConfig = Field(\n OPTIMADEConfig(),\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n
OPTIMADEParseSession (SessionUpdate)
pydantic-model
","text":"OPTIMADE session for the parse strategy.
Source code inoteapi_optimade/models/strategies/parse.py
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession.optimade_config","title":"class OPTIMADEParseSession(SessionUpdate):\n \"\"\"OPTIMADE session for the parse strategy.\"\"\"\n\n optimade_config: Optional[OPTIMADEConfig] = Field(\n None,\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n optimade_response_object: Optional[Response] = Field(\n None,\n description=\"An OPTIMADE Python tools (OPT) pydantic response object.\",\n )\n optimade_response: Optional[Dict[str, Any]] = Field(\n None,\n description=\"An OPTIMADE response as a Python dictionary.\",\n )\n\n class Config:\n \"\"\"Pydantic configuration for `OPTIMADEParseSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
optimade_config: OPTIMADEConfig
pydantic-field
","text":"OPTIMADE configuration. Contains relevant information necessary to perform OPTIMADE queries.
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession.optimade_response","title":"optimade_response: Dict[str, Any]
pydantic-field
","text":"An OPTIMADE response as a Python dictionary.
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession.optimade_response_object","title":"optimade_response_object: Response
pydantic-field
","text":"An OPTIMADE Python tools (OPT) pydantic response object.
"},{"location":"api_reference/models/strategies/parse/#oteapi_optimade.models.strategies.parse.OPTIMADEParseSession.Config","title":"Config
","text":"Pydantic configuration for
Source code inOPTIMADEParseSession
.oteapi_optimade/models/strategies/parse.py
"},{"location":"api_reference/models/strategies/resource/","title":"resource","text":"class Config:\n \"\"\"Pydantic configuration for `OPTIMADEParseSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
Models specific to the resource strategy.
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceConfig","title":"OPTIMADEResourceConfig (ResourceConfig)
pydantic-model
","text":"OPTIMADE-specific resource strategy config.
Source code inoteapi_optimade/models/strategies/resource.py
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession","title":"class OPTIMADEResourceConfig(ResourceConfig):\n \"\"\"OPTIMADE-specific resource strategy config.\"\"\"\n\n accessUrl: OPTIMADEUrl = Field(\n ...,\n description=\"Either a base OPTIMADE URL or a full OPTIMADE URL.\",\n )\n accessService: Literal[\n \"optimade\",\n \"OPTIMADE\",\n \"OPTiMaDe\",\n \"optimade+dlite\",\n \"OPTIMADE+dlite\",\n \"OPTiMaDe+dlite\",\n \"optimade+DLite\",\n \"OPTIMADE+DLite\",\n \"OPTiMaDe+DLite\",\n ] = Field(\n ...,\n description=\"The registered strategy name for OPTIMADEResourceStrategy.\",\n )\n configuration: OPTIMADEConfig = Field(\n OPTIMADEConfig(),\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n
OPTIMADEResourceSession (SessionUpdate)
pydantic-model
","text":"OPTIMADE session for the resource strategy.
Source code inoteapi_optimade/models/strategies/resource.py
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession.optimade_config","title":"class OPTIMADEResourceSession(SessionUpdate):\n \"\"\"OPTIMADE session for the resource strategy.\"\"\"\n\n optimade_config: Optional[OPTIMADEConfig] = Field(\n None,\n description=(\n \"OPTIMADE configuration. Contains relevant information necessary to \"\n \"perform OPTIMADE queries.\"\n ),\n )\n optimade_resources: List[Dict[str, Any]] = Field(\n [],\n description=(\n \"List of OPTIMADE resources (structures, references, errors, ...) returned\"\n \" from the OPTIMADE request.\"\n ),\n )\n optimade_resource_model: str = Field(\n \"\",\n description=(\n \"Importable path to the resource model to be used to parse the OPTIMADE \"\n \"resources in `optimade_resource`. The importable path should be a fully \"\n \"importable path to a module separated by a colon (`:`) to then define the \"\n \"resource model class name. This means one can then do:\\n\\n```python\\n\"\n \"from PACKAGE.MODULE import RESOURCE_CLS\\n```\\nFrom the value \"\n \"`PACKAGE.MODULE:RESOURCE_CLS`\"\n ),\n regex=(\n r\"^([a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)*\" # package.module\n r\":[a-zA-Z][a-zA-Z0-9_]*)?$\" # class\n ),\n )\n\n class Config:\n \"\"\"Pydantic configuration for `OPTIMADEResourceSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
optimade_config: OPTIMADEConfig
pydantic-field
","text":"OPTIMADE configuration. Contains relevant information necessary to perform OPTIMADE queries.
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession.optimade_resource_model","title":"optimade_resource_model: ConstrainedStrValue
pydantic-field
","text":"Importable path to the resource model to be used to parse the OPTIMADE resources in
optimade_resource
. The importable path should be a fully importable path to a module separated by a colon (:
) to then define the resource model class name. This means one can then do:From the valuefrom PACKAGE.MODULE import RESOURCE_CLS\n
PACKAGE.MODULE:RESOURCE_CLS
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession.optimade_resources","title":"optimade_resources: List[Dict[str, Any]]
pydantic-field
","text":"List of OPTIMADE resources (structures, references, errors, ...) returned from the OPTIMADE request.
"},{"location":"api_reference/models/strategies/resource/#oteapi_optimade.models.strategies.resource.OPTIMADEResourceSession.Config","title":"Config
","text":"Pydantic configuration for
Source code inOPTIMADEResourceSession
.oteapi_optimade/models/strategies/resource.py
"},{"location":"api_reference/strategies/filter/","title":"filter","text":"class Config:\n \"\"\"Pydantic configuration for `OPTIMADEResourceSession`.\"\"\"\n\n validate_assignment = True\n arbitrary_types_allowed = True\n
Demo filter strategy.
"},{"location":"api_reference/strategies/filter/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy","title":"OPTIMADEFilterStrategy
dataclass
","text":"Filter Strategy.
Implements strategies:
Source code in
(\"filterType\", \"OPTIMADE\")
(\"filterType\", \"optimade\")
(\"filterType\", \"OPTiMaDe\")
oteapi_optimade/strategies/filter.py
"},{"location":"api_reference/strategies/filter/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy.get","title":"@dataclass\nclass OPTIMADEFilterStrategy:\n \"\"\"Filter Strategy.\n\n **Implements strategies**:\n\n - `(\"filterType\", \"OPTIMADE\")`\n - `(\"filterType\", \"optimade\")`\n - `(\"filterType\", \"OPTiMaDe\")`\n\n \"\"\"\n\n filter_config: OPTIMADEFilterConfig\n\n def initialize(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEFilterSession:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Configuration values, specifically URL query parameters, can be provided to the\n OPTIMADE resource strategy through this filter strategy.\n\n Workflow:\n\n 1. Compile received information.\n 2. Update session with compiled information.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEFilterSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEFilterSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEFilterSession()\n\n if session.optimade_config:\n self.filter_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_config = self.filter_config.configuration.copy()\n\n if not optimade_config.query_parameters:\n optimade_config.query_parameters = OPTIMADEQueryParameters()\n\n if self.filter_config.query:\n LOGGER.debug(\"Setting filter from query.\")\n optimade_config.query_parameters.filter = self.filter_config.query\n\n if self.filter_config.limit:\n LOGGER.debug(\"Setting page_limit from limit.\")\n optimade_config.query_parameters.page_limit = self.filter_config.limit\n\n return session.copy(\n update={\n \"optimade_config\": optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n },\n )\n\n def get(\n self,\n session: \"Optional[Dict[str, Any]]\" = None,\n ) -> SessionUpdate:\n \"\"\"Execute the strategy.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n return SessionUpdate()\n
get(self, session=None)
","text":"Execute the strategy.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/filter.py
"},{"location":"api_reference/strategies/filter/#oteapi_optimade.strategies.filter.OPTIMADEFilterStrategy.initialize","title":"def get(\n self,\n session: \"Optional[Dict[str, Any]]\" = None,\n) -> SessionUpdate:\n \"\"\"Execute the strategy.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n return SessionUpdate()\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Configuration values, specifically URL query parameters, can be provided to the OPTIMADE resource strategy through this filter strategy.
Workflow:
- Compile received information.
- Update session with compiled information.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary context.
None
Returns:
Type DescriptionOPTIMADEFilterSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/filter.py
"},{"location":"api_reference/strategies/parse/","title":"parse","text":"def initialize(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEFilterSession:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Configuration values, specifically URL query parameters, can be provided to the\n OPTIMADE resource strategy through this filter strategy.\n\n Workflow:\n\n 1. Compile received information.\n 2. Update session with compiled information.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the\n session-specific context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEFilterSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEFilterSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEFilterSession()\n\n if session.optimade_config:\n self.filter_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_config = self.filter_config.configuration.copy()\n\n if not optimade_config.query_parameters:\n optimade_config.query_parameters = OPTIMADEQueryParameters()\n\n if self.filter_config.query:\n LOGGER.debug(\"Setting filter from query.\")\n optimade_config.query_parameters.filter = self.filter_config.query\n\n if self.filter_config.limit:\n LOGGER.debug(\"Setting page_limit from limit.\")\n optimade_config.query_parameters.page_limit = self.filter_config.limit\n\n return session.copy(\n update={\n \"optimade_config\": optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n },\n )\n
Demo strategy class for text/json.
"},{"location":"api_reference/strategies/parse/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy","title":"OPTIMADEParseStrategy
dataclass
","text":"Parse strategy for JSON.
Implements strategies:
Source code in
(\"mediaType\", \"application/vnd.optimade+json\")
(\"mediaType\", \"application/vnd.OPTIMADE+json\")
(\"mediaType\", \"application/vnd.OPTiMaDe+json\")
(\"mediaType\", \"application/vnd.optimade+JSON\")
(\"mediaType\", \"application/vnd.OPTIMADE+JSON\")
(\"mediaType\", \"application/vnd.OPTiMaDe+JSON\")
(\"mediaType\", \"application/vnd.optimade\")
(\"mediaType\", \"application/vnd.OPTIMADE\")
(\"mediaType\", \"application/vnd.OPTiMaDe\")
oteapi_optimade/strategies/parse.py
"},{"location":"api_reference/strategies/parse/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy.get","title":"@dataclass\nclass OPTIMADEParseStrategy:\n \"\"\"Parse strategy for JSON.\n\n **Implements strategies**:\n\n - `(\"mediaType\", \"application/vnd.optimade+json\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+json\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+json\")`\n - `(\"mediaType\", \"application/vnd.optimade+JSON\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE+JSON\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe+JSON\")`\n - `(\"mediaType\", \"application/vnd.optimade\")`\n - `(\"mediaType\", \"application/vnd.OPTIMADE\")`\n - `(\"mediaType\", \"application/vnd.OPTiMaDe\")`\n\n \"\"\"\n\n parse_config: OPTIMADEParseConfig\n\n def initialize(self, session: \"Optional[Dict[str, Any]]\" = None) -> SessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return SessionUpdate()\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEParseSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEParseSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEParseSession()\n\n if session.optimade_config:\n self.parse_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n cache = DataCache(self.parse_config.configuration.datacache_config)\n if self.parse_config.downloadUrl in cache:\n response: \"Dict[str, Any]\" = cache.get(self.parse_config.downloadUrl)\n elif (\n self.parse_config.configuration.datacache_config.accessKey\n and self.parse_config.configuration.datacache_config.accessKey in cache\n ):\n response = cache.get(\n self.parse_config.configuration.datacache_config.accessKey\n )\n else:\n download_config = self.parse_config.copy()\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n response = {\"json\": json.loads(cache.get(session.pop(\"key\")))}\n\n if (\n not response.get(\"ok\", True)\n or (\n 200 > response.get(\"status_code\", 200)\n or response.get(\"status_code\", 200) >= 300\n )\n or \"errors\" in response.get(\"json\", {})\n ):\n # Error response\n try:\n response_object = ErrorResponse(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Could not validate an error response.\\nValidationError: \"\n \"%s\\nresponse=%r\",\n exc,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate an error response.\"\n ) from exc\n else:\n # Successful response\n response_model = self.parse_config.downloadUrl.response_model()\n if response_model:\n if not isinstance(response_model, tuple):\n response_model = (response_model,)\n for model_cls in response_model:\n try:\n response_object = model_cls(**response.get(\"json\", {}))\n except ValidationError:\n pass\n else:\n break\n else:\n LOGGER.error(\n \"Could not validate for an expected response model.\\nURL=%r\\n\"\n \"response_models=%r\\nresponse=%s\",\n self.parse_config.downloadUrl,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate for an expected response model.\"\n )\n else:\n # No \"endpoint\" or unknown\n try:\n response_object = Success(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Unknown or unparseable endpoint.\\nValidatonError: %s\\n\"\n \"URL=%r\\nendpoint=%r\\nresponse_model=%r\\nresponse=%s\",\n exc,\n self.parse_config.downloadUrl,\n self.parse_config.downloadUrl.endpoint,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Unknown or unparseable endpoint.\"\n ) from exc\n\n if self.parse_config.configuration.return_object:\n session.optimade_response_object = response_object\n else:\n session.optimade_response = model2dict(response_object)\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
get(self, session=None)
","text":"Request and parse an OPTIMADE response using OPT.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromdownloadUrl
.Workflow:
- Request OPTIMADE response.
- Parse as an OPTIMADE Python tools (OPT) pydantic response model.
Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEParseSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/parse.py
"},{"location":"api_reference/strategies/parse/#oteapi_optimade.strategies.parse.OPTIMADEParseStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEParseSession:\n \"\"\"Request and parse an OPTIMADE response using OPT.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `downloadUrl`.\n\n Workflow:\n\n 1. Request OPTIMADE response.\n 2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEParseSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEParseSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEParseSession()\n\n if session.optimade_config:\n self.parse_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n cache = DataCache(self.parse_config.configuration.datacache_config)\n if self.parse_config.downloadUrl in cache:\n response: \"Dict[str, Any]\" = cache.get(self.parse_config.downloadUrl)\n elif (\n self.parse_config.configuration.datacache_config.accessKey\n and self.parse_config.configuration.datacache_config.accessKey in cache\n ):\n response = cache.get(\n self.parse_config.configuration.datacache_config.accessKey\n )\n else:\n download_config = self.parse_config.copy()\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.DOWNLOAD, download_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n response = {\"json\": json.loads(cache.get(session.pop(\"key\")))}\n\n if (\n not response.get(\"ok\", True)\n or (\n 200 > response.get(\"status_code\", 200)\n or response.get(\"status_code\", 200) >= 300\n )\n or \"errors\" in response.get(\"json\", {})\n ):\n # Error response\n try:\n response_object = ErrorResponse(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Could not validate an error response.\\nValidationError: \"\n \"%s\\nresponse=%r\",\n exc,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate an error response.\"\n ) from exc\n else:\n # Successful response\n response_model = self.parse_config.downloadUrl.response_model()\n if response_model:\n if not isinstance(response_model, tuple):\n response_model = (response_model,)\n for model_cls in response_model:\n try:\n response_object = model_cls(**response.get(\"json\", {}))\n except ValidationError:\n pass\n else:\n break\n else:\n LOGGER.error(\n \"Could not validate for an expected response model.\\nURL=%r\\n\"\n \"response_models=%r\\nresponse=%s\",\n self.parse_config.downloadUrl,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Could not validate for an expected response model.\"\n )\n else:\n # No \"endpoint\" or unknown\n try:\n response_object = Success(**response.get(\"json\", {}))\n except ValidationError as exc:\n LOGGER.error(\n \"Unknown or unparseable endpoint.\\nValidatonError: %s\\n\"\n \"URL=%r\\nendpoint=%r\\nresponse_model=%r\\nresponse=%s\",\n exc,\n self.parse_config.downloadUrl,\n self.parse_config.downloadUrl.endpoint,\n response_model,\n response,\n )\n raise OPTIMADEParseError(\n \"Unknown or unparseable endpoint.\"\n ) from exc\n\n if self.parse_config.configuration.return_object:\n session.optimade_response_object = response_object\n else:\n session.optimade_response = model2dict(response_object)\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionSessionUpdate
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/parse.py
"},{"location":"api_reference/strategies/resource/","title":"resource","text":"def initialize(self, session: \"Optional[Dict[str, Any]]\" = None) -> SessionUpdate:\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n return SessionUpdate()\n
OPTIMADE resource strategy.
"},{"location":"api_reference/strategies/resource/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy","title":"OPTIMADEResourceStrategy
dataclass
","text":"OPTIMADE Resource Strategy.
Implements strategies:
Source code in
(\"accessService\", \"optimade\")
(\"accessService\", \"OPTIMADE\")
(\"accessService\", \"OPTiMaDe\")
(\"accessService\", \"optimade+dlite\")
(\"accessService\", \"OPTIMADE+dlite\")
(\"accessService\", \"OPTiMaDe+dlite\")
(\"accessService\", \"optimade+DLite\")
(\"accessService\", \"OPTIMADE+DLite\")
(\"accessService\", \"OPTiMaDe+DLite\")
oteapi_optimade/strategies/resource.py
"},{"location":"api_reference/strategies/resource/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy.get","title":"@dataclass\nclass OPTIMADEResourceStrategy:\n \"\"\"OPTIMADE Resource Strategy.\n\n **Implements strategies**:\n\n - `(\"accessService\", \"optimade\")`\n - `(\"accessService\", \"OPTIMADE\")`\n - `(\"accessService\", \"OPTiMaDe\")`\n - `(\"accessService\", \"optimade+dlite\")`\n - `(\"accessService\", \"OPTIMADE+dlite\")`\n - `(\"accessService\", \"OPTiMaDe+dlite\")`\n - `(\"accessService\", \"optimade+DLite\")`\n - `(\"accessService\", \"OPTIMADE+DLite\")`\n - `(\"accessService\", \"OPTiMaDe+DLite\")`\n\n \"\"\"\n\n resource_config: OPTIMADEResourceConfig\n\n def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n ) -> \"Union[SessionUpdate, DLiteSessionUpdate]\":\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n ):\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n return SessionUpdate()\n\n def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n ) -> OPTIMADEResourceSession:\n \"\"\"Execute an OPTIMADE query to `accessUrl`.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `accessUrl`.\n\n Workflow:\n 1. Update configuration according to session.\n 2. Deconstruct `accessUrl` (done partly by\n `oteapi_optimade.models.custom_types.OPTIMADEUrl`).\n 3. Reconstruct the complete query URL.\n 4. Send query.\n 5. Store result in data cache.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEResourceSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEResourceSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEResourceSession()\n\n if session.optimade_config:\n self.resource_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_endpoint = self.resource_config.accessUrl.endpoint or \"structures\"\n optimade_query = (\n self.resource_config.configuration.query_parameters\n or OPTIMADEQueryParameters()\n )\n LOGGER.debug(\"resource_config: %r\", self.resource_config)\n\n if self.resource_config.accessUrl.query:\n parsed_query = parse_qs(self.resource_config.accessUrl.query)\n for field, value in parsed_query.items():\n # Only use the latest defined value for any parameter\n if field not in optimade_query.__fields_set__:\n LOGGER.debug(\n \"Setting %r from accessUrl (value=%r)\", field, value[-1]\n )\n setattr(optimade_query, field, value[-1])\n\n LOGGER.debug(\"optimade_query after update: %r\", optimade_query)\n\n optimade_url = OPTIMADEUrl(\n f\"{self.resource_config.accessUrl.base_url}\"\n f\"/{self.resource_config.accessUrl.version or 'v1'}\"\n f\"/{optimade_endpoint}?{optimade_query.generate_query_string()}\"\n )\n LOGGER.debug(\"OPTIMADE URL to be requested: %s\", optimade_url)\n\n # Set cache access key to the full OPTIMADE URL.\n self.resource_config.configuration.datacache_config.accessKey = optimade_url\n\n # Perform query\n response = requests.get(\n optimade_url,\n allow_redirects=True,\n timeout=(3, 27), # timeout in seconds (connect, read)\n )\n\n if optimade_query.response_format and optimade_query.response_format != \"json\":\n raise NotImplementedError(\n \"Can only handle JSON responses for now. Requested response format: \"\n f\"{optimade_query.response_format!r}\"\n )\n\n cache = DataCache(config=self.resource_config.configuration.datacache_config)\n cache.add(\n {\n \"status_code\": response.status_code,\n \"ok\": response.ok,\n \"json\": response.json(),\n }\n )\n\n parse_with_dlite = use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n )\n\n parse_mediaType = (\n \"application/vnd.\"\n f\"{self.resource_config.accessService.split('+', maxsplit=1)[0]}\"\n )\n if parse_with_dlite:\n parse_mediaType += \"+DLite\"\n elif optimade_query.response_format:\n parse_mediaType += f\"+{optimade_query.response_format}\"\n\n parse_config = {\n \"downloadUrl\": optimade_url,\n \"mediaType\": parse_mediaType,\n \"configuration\": {\n \"datacache_config\": self.resource_config.configuration.datacache_config,\n \"return_object\": True,\n },\n }\n\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the session.\"\n )\n optimade_response: \"OPTIMADEResponse\" = session.pop(\"optimade_response_object\")\n if \"optimade_response\" in session and not session.get(\"optimade_response\"):\n del session[\"optimade_response\"]\n\n if isinstance(optimade_response, ErrorResponse):\n optimade_resources = optimade_response.errors\n session.optimade_resource_model = (\n f\"{OptimadeError.__module__}:OptimadeError\"\n )\n elif isinstance(optimade_response, ReferenceResponseMany):\n optimade_resources = [\n Reference(entry).as_dict\n if isinstance(entry, dict)\n else Reference(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, ReferenceResponseOne):\n optimade_resources = [\n Reference(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Reference(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, StructureResponseMany):\n optimade_resources = [\n Structure(entry).as_dict\n if isinstance(entry, dict)\n else Structure(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n elif isinstance(optimade_response, StructureResponseOne):\n optimade_resources = [\n Structure(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Structure(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n else:\n LOGGER.debug(\n \"Could not parse response as errors, references or structures. \"\n \"Response:\\n%r\",\n optimade_response,\n )\n raise OPTIMADEParseError(\n \"Could not retrieve errors, references or structures from response \"\n f\"from {optimade_url}. It could be a valid OPTIMADE API response, \"\n \"however it may not be supported by OTEAPI-OPTIMADE. It may also be an \"\n \"invalid response completely.\"\n )\n\n session.optimade_resources = [\n model2dict(resource) for resource in optimade_resources\n ]\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
get(self, session=None)
","text":"Execute an OPTIMADE query to
accessUrl
.This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in
resource_config.configuration
take precedence over the derived values fromaccessUrl
.Workflow: 1. Update configuration according to session. 2. Deconstruct
accessUrl
(done partly byoteapi_optimade.models.custom_types.OPTIMADEUrl
). 3. Reconstruct the complete query URL. 4. Send query. 5. Store result in data cache.Parameters:
Name Type Description Defaultsession
Optional[Union[SessionUpdate, Dict[str, Any]]]
A session-specific dictionary-like context.
None
Returns:
Type DescriptionOPTIMADEResourceSession
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"api_reference/strategies/resource/#oteapi_optimade.strategies.resource.OPTIMADEResourceStrategy.initialize","title":"def get(\n self, session: \"Optional[Union[SessionUpdate, Dict[str, Any]]]\" = None\n) -> OPTIMADEResourceSession:\n \"\"\"Execute an OPTIMADE query to `accessUrl`.\n\n This method will be called through the strategy-specific endpoint of the\n OTE-API Services.\n\n Configuration values provided in `resource_config.configuration` take\n precedence over the derived values from `accessUrl`.\n\n Workflow:\n 1. Update configuration according to session.\n 2. Deconstruct `accessUrl` (done partly by\n `oteapi_optimade.models.custom_types.OPTIMADEUrl`).\n 3. Reconstruct the complete query URL.\n 4. Send query.\n 5. Store result in data cache.\n\n Parameters:\n session: A session-specific dictionary-like context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if session and isinstance(session, dict):\n session = OPTIMADEResourceSession(**session)\n elif session and isinstance(session, SessionUpdate):\n session = OPTIMADEResourceSession(\n **model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n else:\n session = OPTIMADEResourceSession()\n\n if session.optimade_config:\n self.resource_config.configuration.update(\n model2dict(\n session.optimade_config, exclude_defaults=True, exclude_unset=True\n )\n )\n\n optimade_endpoint = self.resource_config.accessUrl.endpoint or \"structures\"\n optimade_query = (\n self.resource_config.configuration.query_parameters\n or OPTIMADEQueryParameters()\n )\n LOGGER.debug(\"resource_config: %r\", self.resource_config)\n\n if self.resource_config.accessUrl.query:\n parsed_query = parse_qs(self.resource_config.accessUrl.query)\n for field, value in parsed_query.items():\n # Only use the latest defined value for any parameter\n if field not in optimade_query.__fields_set__:\n LOGGER.debug(\n \"Setting %r from accessUrl (value=%r)\", field, value[-1]\n )\n setattr(optimade_query, field, value[-1])\n\n LOGGER.debug(\"optimade_query after update: %r\", optimade_query)\n\n optimade_url = OPTIMADEUrl(\n f\"{self.resource_config.accessUrl.base_url}\"\n f\"/{self.resource_config.accessUrl.version or 'v1'}\"\n f\"/{optimade_endpoint}?{optimade_query.generate_query_string()}\"\n )\n LOGGER.debug(\"OPTIMADE URL to be requested: %s\", optimade_url)\n\n # Set cache access key to the full OPTIMADE URL.\n self.resource_config.configuration.datacache_config.accessKey = optimade_url\n\n # Perform query\n response = requests.get(\n optimade_url,\n allow_redirects=True,\n timeout=(3, 27), # timeout in seconds (connect, read)\n )\n\n if optimade_query.response_format and optimade_query.response_format != \"json\":\n raise NotImplementedError(\n \"Can only handle JSON responses for now. Requested response format: \"\n f\"{optimade_query.response_format!r}\"\n )\n\n cache = DataCache(config=self.resource_config.configuration.datacache_config)\n cache.add(\n {\n \"status_code\": response.status_code,\n \"ok\": response.ok,\n \"json\": response.json(),\n }\n )\n\n parse_with_dlite = use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n )\n\n parse_mediaType = (\n \"application/vnd.\"\n f\"{self.resource_config.accessService.split('+', maxsplit=1)[0]}\"\n )\n if parse_with_dlite:\n parse_mediaType += \"+DLite\"\n elif optimade_query.response_format:\n parse_mediaType += f\"+{optimade_query.response_format}\"\n\n parse_config = {\n \"downloadUrl\": optimade_url,\n \"mediaType\": parse_mediaType,\n \"configuration\": {\n \"datacache_config\": self.resource_config.configuration.datacache_config,\n \"return_object\": True,\n },\n }\n\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).initialize(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n session.update(\n create_strategy(StrategyType.PARSE, parse_config).get(\n model2dict(session, exclude_defaults=True, exclude_unset=True)\n )\n )\n\n if \"optimade_response_object\" not in session:\n raise ValueError(\n \"'optimade_response_object' was expected to be present in the session.\"\n )\n optimade_response: \"OPTIMADEResponse\" = session.pop(\"optimade_response_object\")\n if \"optimade_response\" in session and not session.get(\"optimade_response\"):\n del session[\"optimade_response\"]\n\n if isinstance(optimade_response, ErrorResponse):\n optimade_resources = optimade_response.errors\n session.optimade_resource_model = (\n f\"{OptimadeError.__module__}:OptimadeError\"\n )\n elif isinstance(optimade_response, ReferenceResponseMany):\n optimade_resources = [\n Reference(entry).as_dict\n if isinstance(entry, dict)\n else Reference(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, ReferenceResponseOne):\n optimade_resources = [\n Reference(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Reference(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Reference.__module__}:Reference\"\n elif isinstance(optimade_response, StructureResponseMany):\n optimade_resources = [\n Structure(entry).as_dict\n if isinstance(entry, dict)\n else Structure(entry.dict()).as_dict\n for entry in optimade_response.data\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n elif isinstance(optimade_response, StructureResponseOne):\n optimade_resources = [\n Structure(optimade_response.data).as_dict\n if isinstance(optimade_response.data, dict)\n else Structure(optimade_response.data.dict()).as_dict\n ]\n session.optimade_resource_model = f\"{Structure.__module__}:Structure\"\n else:\n LOGGER.debug(\n \"Could not parse response as errors, references or structures. \"\n \"Response:\\n%r\",\n optimade_response,\n )\n raise OPTIMADEParseError(\n \"Could not retrieve errors, references or structures from response \"\n f\"from {optimade_url}. It could be a valid OPTIMADE API response, \"\n \"however it may not be supported by OTEAPI-OPTIMADE. It may also be an \"\n \"invalid response completely.\"\n )\n\n session.optimade_resources = [\n model2dict(resource) for resource in optimade_resources\n ]\n\n if session.optimade_config and session.optimade_config.query_parameters:\n session = session.copy(\n update={\n \"optimade_config\": session.optimade_config.copy(\n update={\n \"query_parameters\": model2dict(\n session.optimade_config.query_parameters,\n exclude_defaults=True,\n exclude_unset=True,\n )\n }\n )\n }\n )\n\n return session\n
initialize(self, session=None)
","text":"Initialize strategy.
This method will be called through the
/initialize
endpoint of the OTE-API Services.Parameters:
Name Type Description Defaultsession
Optional[Dict[str, Any]]
A session-specific dictionary context.
None
Returns:
Type DescriptionUnion[SessionUpdate, DLiteSessionUpdate]
An update model of key/value-pairs to be stored in the session-specific context from services.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"api_reference/strategies/resource/#oteapi_optimade.strategies.resource.use_dlite","title":"def initialize(\n self, session: \"Optional[Dict[str, Any]]\" = None\n) -> \"Union[SessionUpdate, DLiteSessionUpdate]\":\n \"\"\"Initialize strategy.\n\n This method will be called through the `/initialize` endpoint of the OTE-API\n Services.\n\n Parameters:\n session: A session-specific dictionary context.\n\n Returns:\n An update model of key/value-pairs to be stored in the session-specific\n context from services.\n\n \"\"\"\n if use_dlite(\n self.resource_config.accessService,\n self.resource_config.configuration.use_dlite,\n ):\n return DLiteSessionUpdate(collection_id=get_collection(session).uuid)\n return SessionUpdate()\n
use_dlite(access_service, use_dlite_flag)
","text":"Determine whether DLite should be utilized in the Resource strategy.
Parameters:
Name Type Description Defaultaccess_service
str
The accessService value from the resource's configuration.
requireduse_dlite_flag
bool
The strategy-specific
requireduse_dlite
configuration option.Returns:
Type Descriptionbool
Based on the accessService value, then whether DLite should be used or not.
Source code inoteapi_optimade/strategies/resource.py
"},{"location":"examples/","title":"Overview","text":"def use_dlite(access_service: str, use_dlite_flag: bool) -> bool:\n \"\"\"Determine whether DLite should be utilized in the Resource strategy.\n\n Parameters:\n access_service: The accessService value from the resource's configuration.\n use_dlite_flag: The strategy-specific `use_dlite` configuration option.\n\n Returns:\n Based on the accessService value, then whether DLite should be used or not.\n\n \"\"\"\n if (\n any(dlite_form in access_service for dlite_form in [\"DLite\", \"dlite\"])\n or use_dlite_flag\n ):\n if oteapi_dlite_version is None:\n raise MissingDependency(\n \"OTEAPI-DLite is not found on the system. This is required to use \"\n \"DLite with the OTEAPI-OPTIMADE strategies.\"\n )\n return True\n return False\n
This section provides examples of how to use this OTEAPI plugin to perform OPTIMADE queries and handle the results.
In Use OTEAPI-OPTIMADE with OTElib you can find an example of how to use this plugin with the OTElib client.
It is worth noting that there are several different ways to use the strategies in this plugin. For example, an OPTIMADE query can be provided using the
OPTIMADE
filter strategy, but it can also be provided directly in the URL value of theOPTIMADE
data resource strategy'saccessUlr
parameter. Further, it could be set through aconfiguration
parameter entry to either of these strategies.In the examples only one of these options are given, and this is the same for other aspects: What we believe is the most common and transparent use case is given.
Finally, it is important to note that using OTElib directly is not intended for end users. Using OTElib should be done as a backend task in a web application, and the results should be presented to the end user in a more user friendly way.
"},{"location":"examples/#setup-for-examples","title":"Setup for examples","text":""},{"location":"examples/#prerequisites","title":"Prerequisites","text":"To run the examples locally, you need to have the following tools available (in addition to a working Python 3.9+ installation):
"},{"location":"examples/#jupyter-installation","title":"Jupyter installation","text":"
- Jupyter
- Docker (or similar containerization tool)
To install Jupyter, please refer to the Jupyter documentation. If you want to use
pip
to install Jupyter, you can do so by installing theexamples
extra for this plugin package:pip install oteapi-optimade[examples]\n
This will also install OTElib and any other Python packages you may need for the examples.
"},{"location":"examples/#docker-installation","title":"Docker installation","text":"To install Docker, please refer to the Docker documentation.
"},{"location":"examples/#start-a-local-oteapi-server","title":"Start a local OTEAPI server","text":"When running a local OTEAPI server, you need to ensure the OTEAPI-OPTIMADE plugin is installed. This can be done by using the
OTEAPI_PLUGIN_PACKAGES
environment variables as described in the OTEAPI Services README.There are two methods of starting the server:
- Using Docker
- Using Docker Compose
No matter the method, the server will be available at
"},{"location":"examples/#using-docker","title":"Using Docker","text":"http://localhost:80/
. To check it, go to the/docs
endpoint: localhost:80/docs.There are no extra files needed to start the server using Docker. However, several commands need to be run to start the server, which is a collection of different microservices running in different containers on the same Docker network.
The general setup is outlined in the OTEAPI Services README.
For convenience, the following commands can be used to start the services:
docker network create otenet\ndocker volume create redis-persist\ndocker run \\\n --detach \\\n --name redis \\\n --volume redis-persist:/data \\\n --network otenet \\\n redis:latest\ndocker run \\\n --rm \\\n --network otenet \\\n --detach \\\n --publish 80:8080 \\\n --env OTEAPI_REDIS_TYPE=redis \\\n --env OTEAPI_REDIS_HOST=redis \\\n --env OTEAPI_REDIS_PORT=6379 \\\n --env OTEAPI_INCLUDE_REDISADMIN=False \\\n --env OTEAPI_EXPOSE_SECRETS=True \\\n --env OTEAPI_PLUGIN_PACKAGES=oteapi-optimade \\\n ghcr.io/emmc-asbl/oteapi:latest\n
Note
To use the
"},{"location":"examples/#using-docker-compose","title":"Using Docker Compose","text":"/triples
endpoint, an AllegroGraph triplestore needs to be running. For more information see the OTEAPI Services README to see how to set this up and run it.Download the Docker Compose file from the OTEAPI Services repository:
curl -O https://raw.githubusercontent.com/EMMC-ASBL/oteapi-services/master/docker-compose.yml\n
And either update the
OTEAPI_PLUGIN_PACKAGES
environment variable in the file to includeoteapi-optimade
:# ...\n OTEAPI_PLUGIN_PACKAGES: oteapi-optimade\n# ...\n
Or set the environment variable when starting the services by prefixing it to the Docker Compose command.
Then start the services:
docker compose pull\ndocker compose up --detach\n
Note
When setting the environment variables as a prefix to the
docker compose
command, it is only needed for the command that runs the services:"},{"location":"examples/dlite/","title":"Use DLite strategies from OTEAPI-OPTIMADE","text":"In\u00a0[1]: Copied!OTEAPI_PLUGIN_PACKAGES=oteapi-optimade docker compose up --detach\n
from otelib import OTEClient\n\nclient = OTEClient(\"python\")\nfrom otelib import OTEClient client = OTEClient(\"python\") In\u00a0[2]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE+DLite\",\n accessUrl=\"https://optimade.materialsproject.org\",\n)\n\n# This is equivalent to:\n# data_resource_strategy = client.create_dataresource(\n# accessService=\"OPTIMADE\",\n# accessUrl=\"https://optimade.materialsproject.org\",\n# configuration={\"use_dlite\": True},\n# )\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE+DLite\", accessUrl=\"https://optimade.materialsproject.org\", ) # This is equivalent to: # data_resource_strategy = client.create_dataresource( # accessService=\"OPTIMADE\", # accessUrl=\"https://optimade.materialsproject.org\", # configuration={\"use_dlite\": True}, # ) In\u00a0[3]: Copied!filter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='elements HAS ALL \"Si\",\"O\" AND nelements<=4',\n)\nfilter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='elements HAS ALL \"Si\",\"O\" AND nelements<=4', ) In\u00a0[4]: Copied!import json\n\npipeline = filter_strategy >> data_resource_strategy\nsession = pipeline.get()\nparsed_session = json.loads(session)\nparsed_session.keys()\nimport json pipeline = filter_strategy >> data_resource_strategy session = pipeline.get() parsed_session = json.loads(session) parsed_session.keys()/opt/hostedtoolcache/Python/3.9.18/x64/lib/python3.9/site-packages/optimade/server/config.py:113: UserWarning: Unable to find config file at /home/runner/.optimade.json, using the default settings instead.\n warnings.warn(\nOut[4]:dict_keys(['optimade_config', 'optimade_resources', 'optimade_resource_model', 'collection_id'])In\u00a0[5]: Copied!from importlib import import_module\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\n\nparsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\")\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")\nfrom importlib import import_module import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) parsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\") print(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")The query resulted in 20 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1033911', 'mp-757887', 'mp-1219366', 'mp-733539', 'mp-542090', 'mp-758465', 'mp-560675', 'mp-774171', 'mp-1304778', 'mp-1016821', 'mp-1363556', 'mp-757013', 'mp-683953', 'mp-1020609', 'mp-17612', 'mp-558129', 'mp-1210628', 'mp-1197149', 'mp-21791', 'mp-752892']\nIn\u00a0[6]: Copied!from oteapi_dlite.utils import get_collection\n\ncollection = get_collection(session=parsed_session)\nprint(collection)\nfrom oteapi_dlite.utils import get_collection collection = get_collection(session=parsed_session) print(collection){\n \"653cd8e7-27e7-40d4-b398-40ceed2a7b1a\": {\n \"meta\": \"http://onto-ns.com/meta/0.1/Collection\",\n \"dimensions\": {\n \"nrelations\": 60\n },\n \"properties\": {\n \"relations\": [[\"mp-1033911\", \"_is-a\", \"Instance\"], [\"mp-1033911\", \"_has-uuid\", \"1aa1630b-bf79-4710-a5f2-545d21ac7701\"], [\"mp-1033911\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-757887\", \"_is-a\", \"Instance\"], [\"mp-757887\", \"_has-uuid\", \"c7a38ce8-8211-4e42-a0fa-8c4b16cea42d\"], [\"mp-757887\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1219366\", \"_is-a\", \"Instance\"], [\"mp-1219366\", \"_has-uuid\", \"692d0cdd-6b3a-49eb-b13e-b8a45c786e77\"], [\"mp-1219366\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-733539\", \"_is-a\", \"Instance\"], [\"mp-733539\", \"_has-uuid\", \"100ec662-11cc-432c-9bae-0055afbe6c24\"], [\"mp-733539\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-542090\", \"_is-a\", \"Instance\"], [\"mp-542090\", \"_has-uuid\", \"d4fd483e-5de2-46c9-beba-8edf9ba030c9\"], [\"mp-542090\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-758465\", \"_is-a\", \"Instance\"], [\"mp-758465\", \"_has-uuid\", \"2455b57d-8a46-4938-8dd0-a3afd2aa6f11\"], [\"mp-758465\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-560675\", \"_is-a\", \"Instance\"], [\"mp-560675\", \"_has-uuid\", \"b1876370-f51e-44c8-825c-8fa9df304ed6\"], [\"mp-560675\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-774171\", \"_is-a\", \"Instance\"], [\"mp-774171\", \"_has-uuid\", \"89ce9205-abb3-4605-b96a-9a419d85bf76\"], [\"mp-774171\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1304778\", \"_is-a\", \"Instance\"], [\"mp-1304778\", \"_has-uuid\", \"88e6f9b0-f495-41f6-9b78-7d9d23f7f144\"], [\"mp-1304778\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1016821\", \"_is-a\", \"Instance\"], [\"mp-1016821\", \"_has-uuid\", \"bb3b0b09-ca8b-40e7-a258-bc77948a249a\"], [\"mp-1016821\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1363556\", \"_is-a\", \"Instance\"], [\"mp-1363556\", \"_has-uuid\", \"e3399e4d-925c-4916-b4e2-300ed9a16d1a\"], [\"mp-1363556\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-757013\", \"_is-a\", \"Instance\"], [\"mp-757013\", \"_has-uuid\", \"eab02f6c-fb69-4a5e-9072-1400bfabe1bb\"], [\"mp-757013\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-683953\", \"_is-a\", \"Instance\"], [\"mp-683953\", \"_has-uuid\", \"72f75039-bbcc-4e10-a78d-ed1caefff2ad\"], [\"mp-683953\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1020609\", \"_is-a\", \"Instance\"], [\"mp-1020609\", \"_has-uuid\", \"0d529c7a-0881-4eee-b1ba-94b35e92cc2e\"], [\"mp-1020609\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-17612\", \"_is-a\", \"Instance\"], [\"mp-17612\", \"_has-uuid\", \"55aa5aa7-0eff-4013-a2db-d84b8e97bf38\"], [\"mp-17612\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-558129\", \"_is-a\", \"Instance\"], [\"mp-558129\", \"_has-uuid\", \"74f8f088-4c73-4ea8-b338-24ebbd1ac824\"], [\"mp-558129\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1210628\", \"_is-a\", \"Instance\"], [\"mp-1210628\", \"_has-uuid\", \"ca59b8b4-23c3-4af8-9ebd-fa14025e75e4\"], [\"mp-1210628\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-1197149\", \"_is-a\", \"Instance\"], [\"mp-1197149\", \"_has-uuid\", \"28dbe1d9-91bd-45fc-9278-0047cf5d3253\"], [\"mp-1197149\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-21791\", \"_is-a\", \"Instance\"], [\"mp-21791\", \"_has-uuid\", \"5fcad93f-4187-480f-b58b-992580f12955\"], [\"mp-21791\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"], [\"mp-752892\", \"_is-a\", \"Instance\"], [\"mp-752892\", \"_has-uuid\", \"fe36f224-77e6-4415-90a7-2ffd12aed1d4\"], [\"mp-752892\", \"_has-meta\", \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\"]]\n }\n }\n}\nIn\u00a0[7]: Copied!for inst in collection.get_instances():\n print(inst)\nfor inst in collection.get_instances(): print(inst){\n \"1aa1630b-bf79-4710-a5f2-545d21ac7701\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"38c3b251-a12b-4697-84cf-ab4586558c3b\",\n \"id\": \"mp-1033911\"\n }\n }\n}\n{\n \"c7a38ce8-8211-4e42-a0fa-8c4b16cea42d\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"3df086c2-1750-4cbd-8309-74c0389b5e78\",\n \"id\": \"mp-757887\"\n }\n }\n}\n{\n \"692d0cdd-6b3a-49eb-b13e-b8a45c786e77\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"38eaf31f-adf9-4164-9250-82b2e1308177\",\n \"id\": \"mp-1219366\"\n }\n }\n}\n{\n \"100ec662-11cc-432c-9bae-0055afbe6c24\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"a8458d8c-657d-4db3-868d-7f5825ed5dd5\",\n \"id\": \"mp-733539\"\n }\n }\n}\n{\n \"d4fd483e-5de2-46c9-beba-8edf9ba030c9\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"68ac43eb-dd22-441b-a49e-5bf1cb7b5411\",\n \"id\": \"mp-542090\"\n }\n }\n}\n{\n \"2455b57d-8a46-4938-8dd0-a3afd2aa6f11\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"cdb8c464-8d61-4ab1-bbb0-dea798fcbc72\",\n \"id\": \"mp-758465\"\n }\n }\n}\n{\n \"b1876370-f51e-44c8-825c-8fa9df304ed6\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"b21284fc-16d3-46d5-8e33-ce9245edf281\",\n \"id\": \"mp-560675\"\n }\n }\n}\n{\n \"89ce9205-abb3-4605-b96a-9a419d85bf76\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"7b6c4566-4143-481c-84df-55fe86c88693\",\n \"id\": \"mp-774171\"\n }\n }\n}\n{\n \"88e6f9b0-f495-41f6-9b78-7d9d23f7f144\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"475507e4-0dc0-49a9-a806-2c633e92d525\",\n \"id\": \"mp-1304778\"\n }\n }\n}\n{\n \"bb3b0b09-ca8b-40e7-a258-bc77948a249a\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"5ddfaffa-72ff-4683-84d1-5a32b9539f31\",\n \"id\": \"mp-1016821\"\n }\n }\n}\n{\n \"e3399e4d-925c-4916-b4e2-300ed9a16d1a\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"0051bbb8-293f-46e5-8e67-20b177914e61\",\n \"id\": \"mp-1363556\"\n }\n }\n}\n{\n \"eab02f6c-fb69-4a5e-9072-1400bfabe1bb\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"6b46eaf4-f633-42c2-ad86-2ba7c7e956e4\",\n \"id\": \"mp-757013\"\n }\n }\n}\n{\n \"72f75039-bbcc-4e10-a78d-ed1caefff2ad\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"1677897b-b5c5-40fa-be81-ac67cbe848ae\",\n \"id\": \"mp-683953\"\n }\n }\n}\n{\n \"0d529c7a-0881-4eee-b1ba-94b35e92cc2e\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"b4957da4-3897-4438-aa89-7840637fff13\",\n \"id\": \"mp-1020609\"\n }\n }\n}\n{\n \"55aa5aa7-0eff-4013-a2db-d84b8e97bf38\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"0b4756e2-6179-4e68-9534-990d2c2282dd\",\n \"id\": \"mp-17612\"\n }\n }\n}\n{\n \"74f8f088-4c73-4ea8-b338-24ebbd1ac824\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"52f85e16-89ad-43cf-a42e-2adedf84e32c\",\n \"id\": \"mp-558129\"\n }\n }\n}\n{\n \"ca59b8b4-23c3-4af8-9ebd-fa14025e75e4\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"3a2c4cf1-9c75-4b36-9b70-72e4d67d794f\",\n \"id\": \"mp-1210628\"\n }\n }\n}\n{\n \"28dbe1d9-91bd-45fc-9278-0047cf5d3253\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"bf76d6b4-4b3c-48b0-b194-52bb4e6b7688\",\n \"id\": \"mp-1197149\"\n }\n }\n}\n{\n \"5fcad93f-4187-480f-b58b-992580f12955\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"5c82ae72-f56b-459a-8c8c-be3c4e5d162d\",\n \"id\": \"mp-21791\"\n }\n }\n}\n{\n \"fe36f224-77e6-4415-90a7-2ffd12aed1d4\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructure\",\n \"dimensions\": {\n },\n \"properties\": {\n \"type\": \"structures\",\n \"attributes\": \"a46e0ac1-a3c8-4d0a-86d1-b1951ba570bc\",\n \"id\": \"mp-752892\"\n }\n }\n}\nIn\u00a0[8]: Copied!import dlite\n\nstructure_instances: list[dlite.Instance] = list(collection.get_instances())\n\nstructure_attributes_instance: dlite.Instance = dlite.get_instance(structure_instances[0].attributes)\n\nprint(structure_attributes_instance)\nimport dlite structure_instances: list[dlite.Instance] = list(collection.get_instances()) structure_attributes_instance: dlite.Instance = dlite.get_instance(structure_instances[0].attributes) print(structure_attributes_instance){\n \"38c3b251-a12b-4697-84cf-ab4586558c3b\": {\n \"meta\": \"http://onto-ns.com/meta/1.0/OPTIMADEStructureAttributes\",\n \"dimensions\": {\n \"nelements\": 4,\n \"dimensionality\": 3,\n \"nsites\": 32,\n \"nspecies\": 4,\n \"nstructure_features\": 0\n },\n \"properties\": {\n \"elements\": [\"K\", \"Mg\", \"O\", \"Si\"],\n \"elements_ratios\": [0.03125, 0.4375, 0.03125, 0.5],\n \"chemical_formula_descriptive\": \"KMg14O16Si\",\n \"chemical_formula_reduced\": \"KMg14O16Si\",\n \"chemical_formula_hill\": \"KMg14O16Si\",\n \"chemical_formula_anonymous\": \"A16B14CD\",\n \"dimension_types\": [1, 1, 1],\n \"nperiodic_dimensions\": 3,\n \"lattice_vectors\": [[8.59318, 0, 0], [0, 8.59318, 0], [0, 0, 4.41201]],\n \"cartesian_site_positions\": [[0, 0, 0], [0, 4.29659, 0], [4.29659, 0, 0], [0, 2.1438, 2.20601], [0, 6.44938, 2.20601], [4.29659, 2.13079, 2.20601], [4.29659, 6.46238, 2.20601], [2.1438, 0, 2.20601], [2.13079, 4.29659, 2.20601], [6.44938, 0, 2.20601], [6.46238, 4.29659, 2.20601], [2.12673, 2.12673, 0], [2.12673, 6.46645, 0], [6.46645, 2.12673, 0], [6.46645, 6.46645, 0], [4.29659, 4.29659, 0], [2.37689, 0, 0], [2.32997, 4.29659, 0], [6.21628, 0, 0], [6.2632, 4.29659, 0], [2.16162, 2.16162, 2.20601], [2.16162, 6.43155, 2.20601], [6.43155, 2.16162, 2.20601], [6.43155, 6.43155, 2.20601], [0, 0, 2.20601], [0, 4.29659, 2.20601], [4.29659, 0, 2.20601], [4.29659, 4.29659, 2.20601], [0, 2.37689, 0], [0, 6.21628, 0], [4.29659, 2.32997, 0], [4.29659, 6.2632, 0]],\n \"species\": [\"5d30b3fb-580f-4d79-a8db-30a1790e6ccf\", \"7da719f3-8bb1-4c43-a772-a86042faa374\", \"bb91d74f-891b-495a-8611-82ceec92e66b\", \"2c69e6d7-b7fe-4e23-bf6e-c9477447d7e9\"],\n \"species_at_sites\": [\"K\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Mg\", \"Si\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\", \"O\"],\n \"assemblies\": null,\n \"structure_features\": [],\n \"immutable_id\": \"645d2b74bcd30f748b4746a3\",\n \"last_modified\": \"2019-10-23 12:27:13+00:00\"\n }\n }\n}\n"},{"location":"examples/dlite/#use-dlite-strategies-from-oteapi-optimade","title":"Use DLite strategies from OTEAPI-OPTIMADE\u00b6","text":"This example shows how to use the DLite strategies from the OTEAPI-OPTIMADE plugin.
DLite is a Python library for working with data models and semantics. It is considered to be the default semantic data model backend for use with OTEAPI.
To see more fundamental examples of how to use OTEAPI-OPTIMADE, see the example Use OTEAPI-OPTIMADE with OTElib. OTElib will also be used as a client in the current example. Furthermore, only the HTTP requests-based backend will be used in this example.
"},{"location":"examples/dlite/#setup","title":"Setup\u00b6","text":"Please see the setup instructions in Use OTEAPI-OPTIMADE with OTElib for how to ensure you have a proper environment to run the example in.
"},{"location":"examples/dlite/#example","title":"Example\u00b6","text":"In this example, we will use the DLite strategies to query the Materials Project OPTIMADE API. We are interested in finding all structures that include the elements
"},{"location":"examples/dlite/#create-a-client","title":"Create a client\u00b6","text":"Si
andO
, and that have a maximum of 4 elements in total.Let's start by initializing a client:
"},{"location":"examples/dlite/#data-resource-strategy","title":"Data Resource strategy\u00b6","text":"The general pipeline is the same as it was for Use OTEAPI-OPTIMADE with OTElib:
However, in order to use DLite we need to either reference a specific
accessService
or set a flag in the configuration of the data resource strategy.Note, it is only for the data resource strategy we need to specify the usage of DLite, as the filter strategy is generic and merely a helper for more explicitly setting the query parameters to be used for the underlying OPTIMADE query.
"},{"location":"examples/dlite/#filter-strategy","title":"Filter strategy\u00b6","text":"The OPTIMADE filter query to fulfill the interests outlined above should look like this:
"},{"location":"examples/dlite/#setup-execute-and-inspect-the-pipeline","title":"Setup, execute and inspect the pipeline\u00b6","text":""},{"location":"examples/otelib/","title":"Use OTEAPI-OPTIMADE with OTElib","text":"In\u00a0[1]: Copied!elements HAS ALL \"Si\",\"O\" AND nelements<=4\n
from otelib import OTEClient\n\nclient = OTEClient(\"http://localhost:80\")\nfrom otelib import OTEClient client = OTEClient(\"http://localhost:80\") In\u00a0[2]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE\",\n accessUrl=\"https://optimade.materialsproject.org/\",\n)\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE\", accessUrl=\"https://optimade.materialsproject.org/\", )For the filter strategy, we need to know the OPTIMADE query that we want to execute.
For retrieving all structures with the formula
Al2O3
, we can use the following OPTIMADE filter query:In\u00a0[3]: Copied!chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"\n
filter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"',\n)\nfilter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', )Now we can create the OTE pipeline shown above and execute it.
In\u00a0[4]: Copied!pipeline = filter_strategy >> data_resource_strategy\nsession = pipeline.get()\npipeline = filter_strategy >> data_resource_strategy session = pipeline.get()The returned session is a JSON object we can parse and investigate.
In\u00a0[5]: Copied!import json\n\nparsed_session: dict = json.loads(session)\nprint(parsed_session.keys())\nimport json parsed_session: dict = json.loads(session) print(parsed_session.keys())dict_keys(['optimade_resources', 'optimade_config', 'optimade_resource_model'])\nAs can be seen, there are three keys in the returned session.
In\u00a0[6]: Copied!optimade_config
summarizes the query that has been performed to Materials Project. The OPTIMADE structures are listed under theoptimade_resources
. It is named as such due to there being different OPTIMADE resources, e.g.,structures
,references
,links
, etc. The OPTIMADE Python tools has a useful OPTIMADE Structure model class that can be used to parse the OPTIMADE structures into Python objects as well as validating them according to the OPTIMADE specification. Again, since one can query for different OPTIMADE resources, the specific Python class to use is given inoptimade_resource_model
.from importlib import import_module\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\n\nparsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\")\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")\nfrom importlib import import_module import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) parsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\") print(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")The query resulted in 20 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1228448', 'mp-755483', 'mp-1245081', 'mp-1244878', 'mp-1105018', 'mp-754401', 'mp-1245063', 'mp-1245265', 'mp-684591', 'mp-1244898', 'mp-1245211', 'mp-1245056', 'mp-642363', 'mp-1244930', 'mp-1244937', 'mp-1244967', 'mp-1244954', 'mp-1244874', 'mp-1245008', 'mp-1245023']\nTo find them on the Materials Project website, go to
materialsproject.org/materials/<ID>
, for example: materialsproject.org/materials/mp-1228448.What is more, we can investigate the structure according to the well-defined OPTIMADE structure model attributes. For example, so assert the chemical formula is what we expected, we can check the different chemical formula attributes:
In\u00a0[7]: Copied!structure = parsed_structures[0]\nprint(structure.id)\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nstructure = parsed_structures[0] print(structure.id) for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")mp-1228448\nchemical_formula_descriptive: Al2O3\nchemical_formula_reduced: Al2O3\nchemical_formula_hill: Al2O3\nchemical_formula_anonymous: A3B2\nIn\u00a0[8]: Copied!filter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='elements HAS ALL \"Al\",\"O\"',\n limit=5,\n)\nfilter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='elements HAS ALL \"Al\",\"O\"', limit=5, )For this query, we have added the
limit
parameter to the filter configuration, which will pass apage_limit
query parameter to the OPTIMADE API ensuring that we only retrieve the first 5 structures (it limits each result's page to 5 resources).Let us investigate the result again, checking the list of elements and the chemical formula attributes:
In\u00a0[9]: Copied!pipeline = filter_strategy >> data_resource_strategy\nparsed_session = json.loads(pipeline.get())\nprint(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\")\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\nstructures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in structures]}\")\npipeline = filter_strategy >> data_resource_strategy parsed_session = json.loads(pipeline.get()) print(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\") import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"Their Materials Project IDs are: {[structure.id for structure in structures]}\")The query resulted in 5 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1038042', 'mp-1182891', 'mp-1208627', 'mp-1247835', 'mp-1521059']\nLet us also check the query parameters used for the request to the OPTIMADE API to ensure that the
In\u00a0[10]: Copied!page_limit
query parameter was passed:parsed_session[\"optimade_config\"]\nparsed_session[\"optimade_config\"] Out[10]:{'query_parameters': {'filter': 'elements HAS ALL \"Al\",\"O\"', 'page_limit': 5}}In\u00a0[11]: Copied!structure = structures[0]\nprint(structure.id)\nprint(f\"elements: {structure.elements}\")\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nstructure = structures[0] print(structure.id) print(f\"elements: {structure.elements}\") for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")mp-1038042\nelements: ['Al', 'Cr', 'Mg', 'O']\nchemical_formula_descriptive: AlCrMg30O32\nchemical_formula_reduced: AlCrMg30O32\nchemical_formula_hill: AlCrMg30O32\nchemical_formula_anonymous: A32B30CD\nIn\u00a0[12]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE\",\n accessUrl=\"https://aiida.materialscloud.org/mc3d/optimade\",\n)\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE\", accessUrl=\"https://aiida.materialscloud.org/mc3d/optimade\", ) In\u00a0[13]: Copied!pipeline = filter_strategy >> data_resource_strategy\nparsed_session = json.loads(pipeline.get())\nprint(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\")\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\nstructures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"Their Materials Cloud (AiiDA) IDs are: {[structure.id for structure in structures]}\")\npipeline = filter_strategy >> data_resource_strategy parsed_session = json.loads(pipeline.get()) print(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\") import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"Their Materials Cloud (AiiDA) IDs are: {[structure.id for structure in structures]}\")The query resulted in 5 structures found (on page 1) of the returned data.\nTheir Materials Cloud (AiiDA) IDs are: ['13952', '43703', '43800', '56101', '56270']\nAgain, let's check the query parameters used for the request to the OPTIMADE API to ensure it is equivalent to the previous search:
In\u00a0[14]: Copied!parsed_session[\"optimade_config\"]\nparsed_session[\"optimade_config\"] Out[14]:{'query_parameters': {'filter': 'elements HAS ALL \"Al\",\"O\"', 'page_limit': 5}}In\u00a0[15]: Copied!structure = structures[0]\nprint(structure.id)\nprint(f\"elements: {structure.elements}\")\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nstructure = structures[0] print(structure.id) print(f\"elements: {structure.elements}\") for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")13952\nelements: ['Al', 'O']\nchemical_formula_descriptive: Al2O6\nchemical_formula_reduced: AlO3\nchemical_formula_hill: Al2O6\nchemical_formula_anonymous: A3B\nThe MC3D structures can, unfortunately, not be found easily in the Materials Cloud website, as the ID in the DISCOVER section does not match the ID in the OPTIMADE structure. However, the full OPTIMADE structure can be found at
<OPTIMADE_BASE_URL>/structures/<ID>
, for example: aiida.materialscloud.org/mc3d/optimade/structures/13952.Note
The structure with the OPTIMADE ID 13952 is found with the Materials Cloud DISCOVER ID
In\u00a0[16]: Copied!mc3d-76896
and can be found here.client = OTEClient(\"python\")\nclient = OTEClient(\"python\")Now we can go through the same searches as we did with the HTTP requests-based backend. The result should not change.
In\u00a0[17]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE\",\n accessUrl=\"https://optimade.materialsproject.org/\",\n)\n\nfilter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"',\n)\n\npipeline = filter_strategy >> data_resource_strategy\nsession = pipeline.get()\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE\", accessUrl=\"https://optimade.materialsproject.org/\", ) filter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', ) pipeline = filter_strategy >> data_resource_strategy session = pipeline.get()Setting filter from query.\nSetting filter from query.\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', response_format='json', email_address='', response_fields='', sort='', page_limit=20, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', response_format='json', email_address='', response_fields='', sort='', page_limit=20, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\noptimade_query after update: OPTIMADEQueryParameters(filter='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', response_format='json', email_address='', response_fields='', sort='', page_limit=20, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='chemical_formula_descriptive = \"Al2O3\" OR chemical_formula_reduced = \"Al2O3\" OR chemical_formula_hill = \"Al2O3\"', response_format='json', email_address='', response_fields='', sort='', page_limit=20, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=chemical_formula_descriptive%20%3D%20%22Al2O3%22%20OR%20chemical_formula_reduced%20%3D%20%22Al2O3%22%20OR%20chemical_formula_hill%20%3D%20%22Al2O3%22&response_format=json&page_limit=20&include=references\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=chemical_formula_descriptive%20%3D%20%22Al2O3%22%20OR%20chemical_formula_reduced%20%3D%20%22Al2O3%22%20OR%20chemical_formula_hill%20%3D%20%22Al2O3%22&response_format=json&page_limit=20&include=references\n/home/cwa/.venvs/oteapi-optimade/lib/python3.9/site-packages/optimade/server/config.py:113: UserWarning: Unable to find config file at /home/cwa/.optimade.json, using the default settings instead.\n warnings.warn(\nAs one can see, this backend runs locally within the same Python environment as the notebook. This is useful for development purposes, where the logging messages are shown directly in the output.
In\u00a0[18]: Copied!parsed_session: dict = json.loads(session)\nprint(parsed_session.keys())\nparsed_session: dict = json.loads(session) print(parsed_session.keys())dict_keys(['optimade_config', 'optimade_resources', 'optimade_resource_model'])\nWe get the same keys in the returned session as we did with the HTTP requests-based backend. If we again import the OPTIMADE Structure model class from the OPTIMADE Python tools, we can parse the OPTIMADE structures into Python objects as well as validating them according to the OPTIMADE specification, just as we did with the HTTP requests-based backend.
In\u00a0[19]: Copied!import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\n\nparsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\")\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\")\n\nstructure = parsed_structures[0]\nprint(structure.id)\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) parsed_structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"The query resulted in {len(parsed_structures)} structures found (on page 1) of the returned data.\") print(f\"Their Materials Project IDs are: {[structure.id for structure in parsed_structures]}\") structure = parsed_structures[0] print(structure.id) for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")The query resulted in 20 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1228448', 'mp-755483', 'mp-1245081', 'mp-1244878', 'mp-1105018', 'mp-754401', 'mp-1245063', 'mp-1245265', 'mp-684591', 'mp-1244898', 'mp-1245211', 'mp-1245056', 'mp-642363', 'mp-1244930', 'mp-1244937', 'mp-1244967', 'mp-1244954', 'mp-1244874', 'mp-1245008', 'mp-1245023']\nmp-1228448\nchemical_formula_descriptive: Al2O3\nchemical_formula_reduced: Al2O3\nchemical_formula_hill: Al2O3\nchemical_formula_anonymous: A3B2\nIn\u00a0[20]: Copied!filter_strategy = client.create_filter(\n filterType=\"OPTIMADE\",\n query='elements HAS ALL \"Al\",\"O\"',\n limit=5,\n)\n\npipeline = filter_strategy >> data_resource_strategy\nparsed_session = json.loads(pipeline.get())\nprint(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\")\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\nstructures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"Their Materials Project IDs are: {[structure.id for structure in structures]}\")\nfilter_strategy = client.create_filter( filterType=\"OPTIMADE\", query='elements HAS ALL \"Al\",\"O\"', limit=5, ) pipeline = filter_strategy >> data_resource_strategy parsed_session = json.loads(pipeline.get()) print(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\") import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"Their Materials Project IDs are: {[structure.id for structure in structures]}\")Setting filter from query.\nSetting filter from query.\nSetting filter from query.\nSetting page_limit from limit.\nSetting page_limit from limit.\nSetting page_limit from limit.\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://optimade.materialsproject.org/', base_url='https://optimade.materialsproject.org', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nOPTIMADE URL to be requested: https://optimade.materialsproject.org/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nThe query resulted in 5 structures found (on page 1) of the returned data.\nTheir Materials Project IDs are: ['mp-1038042', 'mp-1182891', 'mp-1208627', 'mp-1247835', 'mp-1521059']\nThe configuration in
In\u00a0[21]: Copied!optimade_config
is quite more extensive than with the HTTP requests-based backend, as it includes more information, automatically setting the default values so they are shown in the config:parsed_session[\"optimade_config\"]\nparsed_session[\"optimade_config\"] Out[21]:{'version': 'v1',\n 'endpoint': 'structures',\n 'query_parameters': {'filter': 'elements HAS ALL \"Al\",\"O\"', 'page_limit': 5},\n 'datacache_config': {'cacheDir': 'oteapi',\n 'accessKey': None,\n 'hashType': 'md5',\n 'expireTime': 86400,\n 'tag': 'optimade'},\n 'return_object': False,\n 'use_dlite': False}Let's look at the first structure again:
In\u00a0[22]: Copied!structure = structures[0]\nprint(structure.id)\nprint(f\"elements: {structure.elements}\")\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\nstructure = structures[0] print(structure.id) print(f\"elements: {structure.elements}\") for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")mp-1038042\nelements: ['Al', 'Cr', 'Mg', 'O']\nchemical_formula_descriptive: AlCrMg30O32\nchemical_formula_reduced: AlCrMg30O32\nchemical_formula_hill: AlCrMg30O32\nchemical_formula_anonymous: A32B30CD\nIn\u00a0[23]: Copied!data_resource_strategy = client.create_dataresource(\n accessService=\"OPTIMADE\",\n accessUrl=\"https://aiida.materialscloud.org/mc3d/optimade\",\n)\n\npipeline = filter_strategy >> data_resource_strategy\nparsed_session = json.loads(pipeline.get())\nprint(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\")\n\nimport_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1)\nResourceClass = getattr(import_module(import_path), class_name)\nstructures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]]\nprint(f\"Their Materials Cloud (AiiDA) IDs are: {[structure.id for structure in structures]}\")\n\nprint(parsed_session[\"optimade_config\"])\n\nstructure = structures[0]\nprint(structure.id)\nprint(f\"elements: {structure.elements}\")\nfor attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"):\n print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")\ndata_resource_strategy = client.create_dataresource( accessService=\"OPTIMADE\", accessUrl=\"https://aiida.materialscloud.org/mc3d/optimade\", ) pipeline = filter_strategy >> data_resource_strategy parsed_session = json.loads(pipeline.get()) print(f\"The query resulted in {len(parsed_session['optimade_resources'])} structures found (on page 1) of the returned data.\") import_path, class_name = parsed_session[\"optimade_resource_model\"].split(\":\", maxsplit=1) ResourceClass = getattr(import_module(import_path), class_name) structures = [ResourceClass(structure) for structure in parsed_session[\"optimade_resources\"]] print(f\"Their Materials Cloud (AiiDA) IDs are: {[structure.id for structure in structures]}\") print(parsed_session[\"optimade_config\"]) structure = structures[0] print(structure.id) print(f\"elements: {structure.elements}\") for attribute in (\"descriptive\", \"reduced\", \"hill\", \"anonymous\"): print(f\"chemical_formula_{attribute}: {getattr(structure, f'chemical_formula_{attribute}', '(not defined)')}\")Setting filter from query.\nSetting filter from query.\nSetting filter from query.\nSetting page_limit from limit.\nSetting page_limit from limit.\nSetting page_limit from limit.\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://aiida.materialscloud.org/mc3d/optimade', base_url='https://aiida.materialscloud.org/mc3d/optimade', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://aiida.materialscloud.org/mc3d/optimade', base_url='https://aiida.materialscloud.org/mc3d/optimade', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\nresource_config: OPTIMADEResourceConfig(user=None, password=None, token=None, client_id=None, client_secret=None, configuration=OPTIMADEConfig(version='v1', endpoint='structures', query_parameters=OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint=''), datacache_config=DataCacheConfig(cacheDir=PosixPath('oteapi'), accessKey=None, hashType='md5', expireTime=86400, tag='optimade'), return_object=False, use_dlite=False), description='Resource Strategy Data Configuration.\\n\\n Important:\\n Either of the pairs of attributes `downloadUrl`/`mediaType` or\\n `accessUrl`/`accessService` MUST be specified.\\n\\n ', downloadUrl=None, mediaType=None, accessUrl=OPTIMADEUrl('https://aiida.materialscloud.org/mc3d/optimade', base_url='https://aiida.materialscloud.org/mc3d/optimade', scheme='https', tld='org', host_type='domain'), accessService='OPTIMADE', license=None, accessRights=None, publisher=None)\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\noptimade_query after update: OPTIMADEQueryParameters(filter='elements HAS ALL \"Al\",\"O\"', response_format='json', email_address='', response_fields='', sort='', page_limit=5, page_offset=0, page_number=None, page_cursor=0, page_above=None, page_below=None, include='references', api_hint='')\nOPTIMADE URL to be requested: https://aiida.materialscloud.org/mc3d/optimade/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nOPTIMADE URL to be requested: https://aiida.materialscloud.org/mc3d/optimade/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nOPTIMADE URL to be requested: https://aiida.materialscloud.org/mc3d/optimade/v1/structures?filter=elements%20HAS%20ALL%20%22Al%22%2C%22O%22&response_format=json&page_limit=5&include=references\nThe query resulted in 5 structures found (on page 1) of the returned data.\nTheir Materials Cloud (AiiDA) IDs are: ['13952', '43703', '43800', '56101', '56270']\n{'version': 'v1', 'endpoint': 'structures', 'query_parameters': {'filter': 'elements HAS ALL \"Al\",\"O\"', 'page_limit': 5}, 'datacache_config': {'cacheDir': 'oteapi', 'accessKey': None, 'hashType': 'md5', 'expireTime': 86400, 'tag': 'optimade'}, 'return_object': False, 'use_dlite': False}\n13952\nelements: ['Al', 'O']\nchemical_formula_descriptive: Al2O6\nchemical_formula_reduced: AlO3\nchemical_formula_hill: Al2O6\nchemical_formula_anonymous: A3B\n"},{"location":"examples/otelib/#use-oteapi-optimade-with-otelib","title":"Use OTEAPI-OPTIMADE with OTElib\u00b6","text":"OTElib is a Python client library for the OTEAPI system. It has two usable backends:
- Python-based. This uses the OTEAPI Core library directly to exectue pipelines. This backend should mainly be used for development purposes.
- HTTP requests-based. This uses the OTEAPI Services library to execute pipelines via HTTP requests to a running OTEAPI server. This backend should be used for production purposes.
This notebook demonstrates how to use OTEAPI-OPTIMADE with OTElib.
"},{"location":"examples/otelib/#example","title":"Example\u00b6","text":"Using OTElib we will do different OPTIMADE queries. First, we will search through the Materials Project database for all structures with the formula
"},{"location":"examples/otelib/#http-requests-based-backend","title":"HTTP requests-based backend\u00b6","text":"Al2O3
. Then, we will search through the Materials Project database for all structures that includeAl
andO
in their chemical formula. Finally, we will search through the Materials Cloud MC3D - Materials Cloud three-dimensional crystals database for all structures that includeAl
andO
in their chemical formula.This backend is equivalent to running in production. It requires a running OTEAPI server.
"},{"location":"examples/otelib/#setup","title":"Setup\u00b6","text":"First, we need to have a running OTEAPI server. If one is not available, we can start one using Docker. See the overview for instructions on how to start a server.
Further documentation about the OTEAPI Service is available as a README on GitHub.
Note
It is advisable to run the OTEAPI server in a separate terminal window, so that we can see the logs from the server. Furthermore, we can stop the server by pressing
Ctrl+C
in the terminal window.After following the instructions, we should have a running OTEAPI server at localhost:80.
"},{"location":"examples/otelib/#create-a-client","title":"Create a client\u00b6","text":""},{"location":"examples/otelib/#search-through-materials-project-for-all-structures-with-the-formula-al2o3","title":"Search through Materials Project for all structures with the formulaAl2O3
\u00b6","text":"To search through Materials Project for all structures with the formula
Al2O3
, we need to create the OTE strategies that we want to use for creating the OTE pipeline:To create the strategies, we need to know how to configure them. This information is available in the OTEAPI Core documentation, specifically for Data Resource strategies and for Filter strategies.
For the data resource strategy, i.e., the OPTIMADE DB strategy, we need to know the base URL of the OPTIMADE API for Materials Project. OPTIMADE has a useful providers dashboard that lists all known OPTIMADE providers and their (sub-)databases. Here we can find the base URL for the Materials Project:
"},{"location":"examples/otelib/#search-through-materials-project-for-all-structures-that-include-al-and-o-in-their-chemical-formula","title":"Search through Materials Project for all structures that includehttps://optimade.materialsproject.org
.Al
andO
in their chemical formula\u00b6","text":"We must use a different OPTIMADE filter query for this search:
elements HAS ALL \"Al\",\"O\"\n
elements
is a structure attribute that lists all elements in the structure. TheHAS ALL
operator matches if, for each value, there is at least one element inelements
equal to that value. (This implements the set operator>=
.)To do this search, we can reuse the OTE pipeline from the previous search, but change the filter strategy.
"},{"location":"examples/otelib/#search-through-mc3d-database-in-materials-cloud-for-all-structures-that-include-al-and-o-in-their-chemical-formula","title":"Search through MC3D database in Materials Cloud for all structures that includeAl
andO
in their chemical formula\u00b6","text":"Finally, let us reuse the same filter strategy we used for the previous search, but change the data resource strategy to point to the MC3D database in Materials Cloud.
The base URL for the MC3D database is
"},{"location":"examples/otelib/#python-based-backend","title":"Python-based backend\u00b6","text":"https://aiida.materialscloud.org/mc3d/optimade
as found in the providers dashboard.With this backend, we do not need a running OTEAPI server.
"},{"location":"examples/otelib/#create-a-client","title":"Create a client\u00b6","text":""},{"location":"examples/otelib/#search-through-materials-project-for-all-structures-with-the-formula-al2o3","title":"Search through Materials Project for all structures with the formulaAl2O3
\u00b6","text":""},{"location":"examples/otelib/#search-through-materials-project-for-all-structures-that-include-al-and-o-in-their-chemical-formula","title":"Search through Materials Project for all structures that includeAl
andO
in their chemical formula\u00b6","text":""},{"location":"examples/otelib/#search-through-mc3d-database-in-materials-cloud-for-all-structures-that-include-al-and-o-in-their-chemical-formula","title":"Search through MC3D database in Materials Cloud for all structures that includeAl
andO
in their chemical formula\u00b6","text":""}]} \ No newline at end of file diff --git a/latest/sitemap.xml.gz b/latest/sitemap.xml.gz index f08d3b6..37911cd 100644 Binary files a/latest/sitemap.xml.gz and b/latest/sitemap.xml.gz differ