55
66if TYPE_CHECKING :
77 from pathlib import Path
8+ from typing import Any
9+
10+ from dlite import Instance
811
912
1013@pytest .mark .parametrize ("return_object" , [True , False ])
1114def test_parse (static_files : "Path" , return_object : bool ) -> None :
1215 """Test parsing."""
16+ from datetime import datetime
17+ from enum import Enum
1318 import json
1419
20+ from numpy import ndarray
21+ from optimade .adapters import Structure
1522 from oteapi .datacache import DataCache
1623 from oteapi_dlite .utils import get_collection
1724
1825 from oteapi_optimade .dlite .parse import OPTIMADEDLiteParseStrategy
19- from oteapi_optimade .models import OPTIMADEDLiteParseConfig
2026 from oteapi_optimade .models .custom_types import OPTIMADEUrl
2127
2228 url = OPTIMADEUrl (
2329 "https://example.org/some/base/v0.1/optimade/v1/structures"
2430 '?filter=elements HAS ALL "Si","O"&sort=nelements&page_limit=2'
2531 )
26- config = OPTIMADEDLiteParseConfig (
27- ** {
28- "mediaType" : "application/vnd.OPTIMADE+DLite" ,
29- "downloadUrl" : url ,
30- "configuration" : {
31- "datacache_config" : {
32- "expireTime" : 60 * 60 * 24 ,
33- "tag" : "optimade" ,
34- "accessKey" : url ,
35- },
36- "return_object" : return_object ,
32+ config = {
33+ "mediaType" : "application/vnd.OPTIMADE+DLite" ,
34+ "downloadUrl" : url ,
35+ "configuration" : {
36+ "datacache_config" : {
37+ "expireTime" : 60 * 60 * 24 ,
38+ "tag" : "optimade" ,
39+ "accessKey" : url ,
3740 },
38- }
39- )
41+ "return_object" : return_object ,
42+ },
43+ }
4044
41- cache = DataCache (config . configuration . datacache_config )
45+ cache = DataCache (config [ " configuration" ][ " datacache_config" ] )
4246 sample_file = static_files / "optimade_response.json"
47+ response_json : dict [str , "Any" ] = json .loads (sample_file .read_bytes ())
4348 cache .add (
4449 {
4550 "status_code" : 200 ,
4651 "ok" : True ,
47- "json" : json . loads ( sample_file . read_bytes ()) ,
52+ "json" : response_json ,
4853 }
4954 )
5055
@@ -53,3 +58,124 @@ def test_parse(static_files: "Path", return_object: bool) -> None:
5358
5459 dlite_collection = get_collection (session )
5560 assert dlite_collection
61+
62+ assert len (list (dlite_collection .get_labels ())) == len (response_json ["data" ])
63+
64+ for structure in response_json ["data" ]:
65+ optimade_structure = Structure (structure )
66+ dlite_collection_labels = list (dlite_collection .get_labels ())
67+
68+ # The structure `id` is used as the label for the instance in the DLite
69+ # collection
70+ assert optimade_structure .id in dlite_collection_labels
71+
72+ dlite_structure : "Instance" = dlite_collection [optimade_structure .id ]
73+
74+ ## Go over other top-level non-container keys in the OPTIMADE structure
75+ assert dlite_structure .type == optimade_structure .type
76+
77+ ## attributes
78+
79+ # Avoid attributes with special model values for now
80+ model_values = ("assemblies" , "species" )
81+
82+ for field in optimade_structure .attributes .__fields__ :
83+ if field in model_values :
84+ continue
85+
86+ expected_value = getattr (optimade_structure .attributes , field )
87+
88+ # Ensure expected values come in the right format
89+ if expected_value and isinstance (expected_value , list ):
90+ # We expect that all internal types in the list are the same
91+ if isinstance (expected_value [0 ], Enum ):
92+ expected_value = [value .value for value in expected_value ]
93+ if isinstance (expected_value [0 ], datetime ):
94+ expected_value = [
95+ value .isoformat (sep = " " ) for value in expected_value
96+ ]
97+
98+ if isinstance (expected_value , Enum ):
99+ expected_value = expected_value .value
100+ if isinstance (expected_value , datetime ):
101+ expected_value = expected_value .isoformat (sep = " " )
102+
103+ dlite_value = getattr (dlite_structure .attributes , field )
104+
105+ # Convert NumPy NDArrays to lists
106+ if isinstance (dlite_value , ndarray ):
107+ dlite_value = dlite_value .tolist ()
108+ # Convert string "None" values to actual Python None values
109+ if isinstance (dlite_value , str ) and dlite_value == "None" :
110+ dlite_value = None
111+
112+ assert dlite_value == expected_value , f"Field: { field } "
113+
114+ # Special attributes
115+ for field in model_values :
116+ # The model value fields are lists of models
117+ expected_value = getattr (optimade_structure .attributes , field )
118+ if expected_value is None :
119+ assert getattr (dlite_structure .attributes , field ) is None
120+ continue
121+
122+ assert isinstance (expected_value , list )
123+ assert isinstance (getattr (dlite_structure .attributes , field ), ndarray )
124+
125+ for i , entry in enumerate (getattr (dlite_structure .attributes , field )):
126+ for sub_field in getattr (optimade_structure .attributes , field )[
127+ 0
128+ ].__fields__ :
129+ expected_sub_value = getattr (expected_value [i ], sub_field )
130+ dlite_sub_value = getattr (entry , sub_field )
131+
132+ # Ensure expected values come in the right format
133+ if expected_sub_value and isinstance (expected_sub_value , list ):
134+ # We expect that all internal types in the list are the same
135+ if isinstance (expected_sub_value [0 ], Enum ):
136+ expected_sub_value = [
137+ value .value for value in expected_sub_value
138+ ]
139+ if isinstance (expected_sub_value [0 ], datetime ):
140+ expected_sub_value = [
141+ value .isoformat (sep = " " ) for value in expected_sub_value
142+ ]
143+
144+ if isinstance (expected_sub_value , Enum ):
145+ expected_sub_value = expected_sub_value .value
146+ if isinstance (expected_sub_value , datetime ):
147+ expected_sub_value = expected_sub_value .isoformat (sep = " " )
148+
149+ # Convert NumPy NDArrays to lists
150+ if isinstance (dlite_sub_value , ndarray ):
151+ dlite_sub_value = dlite_sub_value .tolist ()
152+ # Convert string "None" values to actual Python None values
153+ if isinstance (dlite_sub_value , str ) and dlite_sub_value == "None" :
154+ dlite_sub_value = None
155+
156+ # If an optional field is not present in the data, it will be None.
157+ # In DLite it will always be instantiated with the default values.
158+ # This is an issue with a shaped properties.
159+ # Here we update the expected_sub_value accordingly, with respect
160+ # to specific known optional sub-fields.
161+
162+ # species.mass
163+ if field == "species" and sub_field == "mass" :
164+ if expected_sub_value is None :
165+ expected_sub_value = [0.0 ] * len (
166+ expected_value [i ].chemical_symbols
167+ )
168+
169+ # species.attached
170+ if field == "species" and sub_field == "attached" :
171+ if expected_sub_value is None :
172+ expected_sub_value = []
173+
174+ # species.nattached
175+ if field == "species" and sub_field == "nattached" :
176+ if expected_sub_value is None :
177+ expected_sub_value = []
178+
179+ assert dlite_sub_value == (
180+ expected_sub_value or []
181+ ), f"Field: { field } , sub-field: { sub_field } "
0 commit comments