Skip to content

Commit 11cc921

Browse files
authored
Merge branch 'master' into get-base-url-with-root-path
2 parents 863c0bb + 0675ebb commit 11cc921

File tree

34 files changed

+1235
-350
lines changed

34 files changed

+1235
-350
lines changed

.github/workflows/deps_lint.yml

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ jobs:
141141

142142
services:
143143
mongo:
144-
image: mongo:4.2
144+
image: mongo:4
145145
ports:
146146
- 27017:27017
147147
postgres:
@@ -156,6 +156,14 @@ jobs:
156156
--health-retries 5
157157
ports:
158158
- 5432:5432
159+
elasticsearch:
160+
image: elasticsearch:6.8.13
161+
ports:
162+
- 9200:9200
163+
- 9300:9300
164+
env:
165+
discovery.type: single-node
166+
159167

160168
steps:
161169
- uses: actions/checkout@v2
@@ -176,20 +184,29 @@ jobs:
176184
pip install -r requirements.txt
177185
pip install -r requirements-dev.txt
178186
187+
- name: Run all tests (using `mongomock`)
188+
run: pytest -rs -vvv --cov=./optimade/ --cov-report=xml tests/
189+
env:
190+
OPTIMADE_DATABASE_BACKEND: 'mongomock'
191+
192+
179193
- name: Run server tests (using a real MongoDB)
180-
run: pytest -rs -vvv --cov=./optimade/ --cov-report=xml tests/server
194+
run: pytest -rs -vvv --cov=./optimade/ --cov-report=xml --cov-append tests/server
181195
env:
182-
OPTIMADE_CI_FORCE_MONGO: 1
196+
OPTIMADE_DATABASE_BACKEND: 'mongodb'
183197

184-
- name: Run all tests (using `mongomock`)
185-
run: pytest -rs -vvv --cov=./optimade/ --cov-report=xml --cov-append tests/
198+
- name: Run server tests (using Elasticsearch)
199+
run: pytest -rs -vvv --cov=./optimade/ --cov-report=xml --cov-append tests/server
186200
env:
187-
OPTIMADE_CI_FORCE_MONGO: 0
201+
OPTIMADE_DATABASE_BACKEND: 'elastic'
188202

189203
- name: Install adapter conversion dependencies
190204
run: |
191205
pip install -r requirements-client.txt
192206
# AiiDA-specific
207+
# sqlalchemy install is temporary and has been fixed in aiidateam/aiida-core#4809,
208+
# but is still not released in a new AiiDA version.
209+
pip install sqlalchemy==1.3.23
193210
reentry scan
194211
195212
- name: Setup up environment for AiiDA
@@ -200,9 +217,25 @@ jobs:
200217
- name: Run previously skipped tests for adapter conversion
201218
run: pytest -rs -vvv --cov=./optimade/ --cov-report=xml --cov-append tests/adapters/
202219

203-
- name: Run tests for validator only to assess coverage
220+
- name: Run tests for validator only to assess coverage (mongomock)
204221
if: matrix.python-version == 3.8
205-
run: pytest -rs --cov=./optimade/ --cov-report=xml:validator_cov.xml tests/server/test_server_validation.py
222+
run: pytest -rs --cov=./optimade/ --cov-report=xml:validator_cov.xml --cov-append tests/server/test_server_validation.py
223+
env:
224+
OPTIMADE_DATABASE_BACKEND: 'mongomock'
225+
226+
- name: Run tests for validator only to assess coverage (Elasticsearch)
227+
if: matrix.python-version == 3.8
228+
run: pytest -rs --cov=./optimade/ --cov-report=xml:validator_cov.xml --cov-append tests/server/test_server_validation.py
229+
env:
230+
OPTIMADE_DATABASE_BACKEND: 'elastic'
231+
OPTIMADE_INSERT_TEST_DATA: false # Must be specified as previous steps will have already inserted the test data
232+
233+
- name: Run tests for validator only to assess coverage (MongoDB)
234+
if: matrix.python-version == 3.8
235+
run: pytest -rs --cov=./optimade/ --cov-report=xml:validator_cov.xml --cov-append tests/server/test_server_validation.py
236+
env:
237+
OPTIMADE_DATABASE_BACKEND: 'mongodb'
238+
OPTIMADE_INSERT_TEST_DATA: false # Must be specified as previous steps will have already inserted the test data
206239

207240
- name: Upload coverage to Codecov
208241
if: matrix.python-version == 3.8 && github.repository == 'Materials-Consortia/optimade-python-tools'
@@ -238,10 +271,13 @@ jobs:
238271
pip install -U setuptools
239272
pip install -e .
240273
pip install -r requirements.txt
274+
pip install -r requirements-dev.txt
241275
pip install -r requirements-docs.txt
242276
243277
- name: Build
244-
run: mkdocs build --strict --verbose
278+
run: |
279+
invoke create-api-reference-docs
280+
mkdocs build --strict --verbose
245281
246282
test_build:
247283
runs-on: ubuntu-latest

INSTALL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ py.test
4444
# Install pre-commit environment (e.g., auto-formats code on `git commit`)
4545
pre-commit install
4646

47-
# Optional: Install MongoDB (and set `use_real_mongo = true`)
47+
# Optional: Install MongoDB (and set `database_backend = mongodb`)
4848
# Below method installs in conda environment and
4949
# - starts server in background
5050
# - ensures and uses ~/dbdata directory to store data

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ The aim of OPTIMADE is to develop a common API, compliant with the [JSON API 1.0
1111
This is to enable interoperability among databases that contain calculated properties of existing and hypothetical materials.
1212

1313
This repository contains a library of tools for implementing and consuming [OPTIMADE](https://www.optimade.org) APIs using Python.
14-
It also contains a server validator tool, which may be called from the shell or used as a GitHub Action from [optimade-validator-action](https://github.com/Materials-Consortia/optimade-validator-action).
15-
16-
_Disclaimer_: While the package supports `elasticsearch-dsl` v6 & v7 and `django` v2 & v3, all tests are performed with the latest supported version.
17-
If you experience any issues with the older versions, you are most welcome to contribute to the repository (see below under [Contributing](#contributing)).
14+
Server implementations can make use of the supported MongoDB (v4) and Elasticsearch (v6) database backends, or plug in a custom backend implementation.
15+
The package also contains a server validator tool, which may be called from the shell (`optimade-validator`) or used as a GitHub Action from [optimade-validator-action](https://github.com/Materials-Consortia/optimade-validator-action).
1816

1917
## Status
2018

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# elasticsearch
2+
3+
::: optimade.server.entry_collections.elasticsearch

docs/static/default_config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"config_file": "~/.optimade.json",
33
"debug": false,
4-
"use_real_mongo": false,
4+
"insert_test_data": true,
55
"mongo_database": "optimade",
66
"mongo_uri": "localhost:27017",
77
"links_collection": "links",

optimade/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,21 @@
11
__version__ = "0.13.3"
22
__api_version__ = "1.0.1"
3+
4+
5+
import sys
6+
7+
if sys.version_info.minor == 6:
8+
# Python 3.6
9+
import warnings
10+
11+
warnings.filterwarnings(
12+
action="once",
13+
message=r"v0\.14 of the `optimade` package.*",
14+
category=DeprecationWarning,
15+
append=False,
16+
)
17+
warnings.warn(
18+
"v0.14 of the `optimade` package will be the last to support Python 3.6. "
19+
"Please upgrade to Python 3.7+ to use v0.15 and later versions of `optimade`.",
20+
DeprecationWarning,
21+
)

optimade/filtertransformers/django.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import warnings
22

33
warnings.warn(
4-
"Django functionality is deprecated and will be removed in later versions (unless support is requested).",
4+
"Django functionality is deprecated and will be removed in a later version (unless support is requested).",
55
DeprecationWarning,
66
stacklevel=2,
77
)

optimade/filtertransformers/elasticsearch.py

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from lark import v_args
44
from elasticsearch_dsl import Q, Text, Keyword, Integer, Field
5-
from optimade.models import CHEMICAL_SYMBOLS, ATOMIC_NUMBERS
65
from optimade.filtertransformers import BaseTransformer
76
from optimade.server.exceptions import BadRequest
87

@@ -14,7 +13,7 @@
1413
_has_operators = {"ALL": "must", "ANY": "should"}
1514
_length_quantities = {
1615
"elements": "nelements",
17-
"elements_rations": "nelements",
16+
"elements_ratios": "nelements",
1817
"dimension_types": "dimension_types",
1918
}
2019

@@ -130,42 +129,50 @@ def _has_query_op(self, quantities, op, predicate_zip_list):
130129
# in elastic search. Only supported for elements, where we can construct
131130
# an anonymous "formula" based on elements sorted by order number and
132131
# can do a = comparision to check if all elements are contained
133-
if len(quantities) > 1:
134-
raise NotImplementedError("HAS ONLY is not supported with zip")
135-
quantity = quantities[0]
136132

137-
if quantity.has_only_quantity is None:
138-
raise NotImplementedError(
139-
"HAS ONLY is not supported by %s" % quantity.name
140-
)
141-
142-
def values():
143-
for predicates in predicate_zip_list:
144-
if len(predicates) != 1:
145-
raise NotImplementedError("Tuples not supported in HAS ONLY")
146-
op, value = predicates[0]
147-
if op != "=":
148-
raise NotImplementedError(
149-
"Predicated not supported in HAS ONLY"
150-
)
151-
if not isinstance(value, str):
152-
raise NotImplementedError("Only strings supported in HAS ONLY")
153-
yield value
154-
155-
try:
156-
order_numbers = list([ATOMIC_NUMBERS[element] for element in values()])
157-
order_numbers.sort()
158-
value = "".join(
159-
[CHEMICAL_SYMBOLS[number - 1] for number in order_numbers]
160-
)
161-
except KeyError:
162-
raise NotImplementedError(
163-
"HAS ONLY is only supported for chemical symbols"
164-
)
133+
# @ml-evs: Disabling this HAS ONLY workaround as tests are not passing
134+
raise NotImplementedError(
135+
"HAS ONLY queries are not currently supported by the Elasticsearch backend."
136+
)
165137

166-
return Q("term", **{quantity.has_only_quantity.name: value})
138+
# from optimade.models import CHEMICAL_SYMBOLS, ATOMIC_NUMBERS
139+
140+
# if len(quantities) > 1:
141+
# raise NotImplementedError("HAS ONLY is not supported with zip")
142+
# quantity = quantities[0]
143+
144+
# if quantity.has_only_quantity is None:
145+
# raise NotImplementedError(
146+
# "HAS ONLY is not supported by %s" % quantity.name
147+
# )
148+
149+
# def values():
150+
# for predicates in predicate_zip_list:
151+
# if len(predicates) != 1:
152+
# raise NotImplementedError("Tuples not supported in HAS ONLY")
153+
# op, value = predicates[0]
154+
# if op != "=":
155+
# raise NotImplementedError(
156+
# "Predicated not supported in HAS ONLY"
157+
# )
158+
# if not isinstance(value, str):
159+
# raise NotImplementedError("Only strings supported in HAS ONLY")
160+
# yield value
161+
162+
# try:
163+
# order_numbers = list([ATOMIC_NUMBERS[element] for element in values()])
164+
# order_numbers.sort()
165+
# value = "".join(
166+
# [CHEMICAL_SYMBOLS[number - 1] for number in order_numbers]
167+
# )
168+
# except KeyError:
169+
# raise NotImplementedError(
170+
# "HAS ONLY is only supported for chemical symbols"
171+
# )
172+
173+
# return Q("term", **{quantity.has_only_quantity.name: value})
167174
else:
168-
raise NotImplementedError
175+
raise NotImplementedError(f"Unrecognised operation {op}.")
169176

170177
queries = [
171178
self._has_query(quantities, predicates) for predicates in predicate_zip_list
@@ -320,12 +327,15 @@ def set_zip_op_rhs(self, args):
320327
return lambda quantity: self._has_query_op([quantity] + add_on, op, values)
321328

322329
def property_zip_addon(self, args):
330+
raise NotImplementedError("Correlated list queries are not supported.")
323331
return args
324332

325333
def value_zip(self, args):
334+
raise NotImplementedError("Correlated list queries are not supported.")
326335
return self.value_list(args)
327336

328337
def value_zip_list(self, args):
338+
raise NotImplementedError("Correlated list queries are not supported.")
329339
return args
330340

331341
def value_list(self, args):

optimade/filtertransformers/mongo.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ def value_list(self, arg):
6161

6262
def value_zip(self, arg):
6363
# value_zip: [ OPERATOR ] value ":" [ OPERATOR ] value (":" [ OPERATOR ] value)*
64-
raise NotImplementedError
64+
raise NotImplementedError("Correlated list queries are not supported.")
6565

6666
def value_zip_list(self, arg):
6767
# value_zip_list: value_zip ( "," value_zip )*
68-
raise NotImplementedError
68+
raise NotImplementedError("Correlated list queries are not supported.")
6969

7070
def expression(self, arg):
7171
# expression: expression_clause ( OR expression_clause )
@@ -158,11 +158,11 @@ def length_op_rhs(self, arg):
158158
def set_zip_op_rhs(self, arg):
159159
# set_zip_op_rhs: property_zip_addon HAS ( value_zip | ONLY value_zip_list | ALL value_zip_list |
160160
# ANY value_zip_list )
161-
raise NotImplementedError
161+
raise NotImplementedError("Correlated list queries are not supported.")
162162

163163
def property_zip_addon(self, arg):
164164
# property_zip_addon: ":" property (":" property)*
165-
raise NotImplementedError
165+
raise NotImplementedError("Correlated list queries are not supported.")
166166

167167
def _recursive_expression_phrase(self, arg):
168168
"""Helper function for parsing `expression_phrase`. Recursively sorts out
@@ -226,15 +226,15 @@ def _apply_aliases(self, filter_: dict) -> dict:
226226
return filter_
227227

228228
def check_for_alias(prop, expr):
229-
return self.mapper.alias_for(prop) != prop
229+
return self.mapper.get_backend_field(prop) != prop
230230

231231
def apply_alias(subdict, prop, expr):
232232
if isinstance(subdict, dict):
233-
subdict[self.mapper.alias_for(prop)] = self._apply_aliases(
233+
subdict[self.mapper.get_backend_field(prop)] = self._apply_aliases(
234234
subdict.pop(prop)
235235
)
236236
elif isinstance(subdict, str):
237-
subdict = self.mapper.alias_for(subdict)
237+
subdict = self.mapper.get_backend_field(subdict)
238238

239239
return subdict
240240

@@ -396,7 +396,7 @@ def replace_str_id_with_objectid(subdict, prop, expr):
396396
val = subdict[prop][operator]
397397
if operator not in ("$eq", "$ne"):
398398
if self.mapper is not None:
399-
prop = self.mapper.alias_of(prop)
399+
prop = self.mapper.get_optimade_field(prop)
400400
raise NotImplementedError(
401401
f"Operator {operator} not supported for query on field {prop!r}, can only test for equality"
402402
)
@@ -417,7 +417,7 @@ def _apply_mongo_date_filter(self, filter_: dict) -> dict:
417417
def check_for_timestamp_field(prop, _):
418418
""" Find cases where the query dict is operating on a timestamp field. """
419419
if self.mapper is not None:
420-
prop = self.mapper.alias_of(prop)
420+
prop = self.mapper.get_optimade_field(prop)
421421
return prop == "last_modified"
422422

423423
def replace_str_date_with_datetime(subdict, prop, expr):

0 commit comments

Comments
 (0)