diff --git a/README.md b/README.md index 549ac9fe..a64061ee 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ICAT API to interface with the Data Gateway ## Requirements -All requirements can be installed with `pip install -r requirements.txt` +All requirements can be installed with `pip install -r requirements.txt`, and all development requirements can be installed with `pip install -r dev-requirements.txt` The required python libraries: - [SQLAlchemy](https://www.sqlalchemy.org/) @@ -30,8 +30,14 @@ The required python libraries: ## Setup and running the API The database connection needs to be set up first. This is set in config.json, an example config file called `config.json.example` is provided. +Ideally the API would be run with: +`python -m src.main` +However it can be run with the flask run command as shown below: + + +**Warning: the host, port and debug config options will not be respected when the API is run this way** -To run the API from the command line, the enviroment variable `FLASK_APP` should be set to `src/main.py`. Once this is +To use `flask run`, the enviroment variable `FLASK_APP` should be set to `src/main.py`. Once this is set the API can be run with `flask run` while inside the root directory of the project. The `flask run` command gets installed with flask. Examples shown: diff --git a/common/database_helpers.py b/common/database_helpers.py index 4c2c0f34..320bf181 100644 --- a/common/database_helpers.py +++ b/common/database_helpers.py @@ -6,6 +6,7 @@ from sqlalchemy.orm import aliased from common.exceptions import MissingRecordError, BadFilterError, BadRequestError, MultipleIncludeError +from common.models import db_models from common.models.db_models import INVESTIGATIONUSER, INVESTIGATION, INSTRUMENT, FACILITYCYCLE, \ INVESTIGATIONINSTRUMENT, FACILITY from common.session_manager import session_manager @@ -147,21 +148,50 @@ class WhereFilter(QueryFilter): def __init__(self, field, value, operation): self.field = field + self.included_field = None + self.included_included_field = None + self._set_filter_fields() self.value = value self.operation = operation + def _set_filter_fields(self): + if self.field.count(".") == 1: + self.included_field = self.field.split(".")[1] + self.field = self.field.split(".")[0] + + if self.field.count(".") == 2: + self.included_included_field = self.field.split(".")[2] + self.included_field = self.field.split(".")[1] + self.field = self.field.split(".")[0] + + def apply_filter(self, query): + try: + field = getattr(query.table, self.field) + except AttributeError: + raise BadFilterError(f"Bad WhereFilter requested") + + if self.included_included_field: + included_table = getattr(db_models, self.field) + included_included_table = getattr(db_models, self.included_field) + query.base_query = query.base_query.join(included_table).join(included_included_table) + field = getattr(included_included_table, self.included_included_field) + + elif self.included_field: + included_table = getattr(db_models, self.field) + query.base_query = query.base_query.join(included_table) + field = getattr(included_table, self.included_field) if self.operation == "eq": - query.base_query = query.base_query.filter(getattr(query.table, self.field) == self.value) + query.base_query = query.base_query.filter(field == self.value) elif self.operation == "like": - query.base_query = query.base_query.filter(getattr(query.table, self.field).like(f"%{self.value}%")) + query.base_query = query.base_query.filter(field.like(f"%{self.value}%")) elif self.operation == "lte": - query.base_query = query.base_query.filter(getattr(query.table, self.field) <= self.value) + query.base_query = query.base_query.filter(field <= self.value) elif self.operation == "gte": - query.base_query = query.base_query.filter(getattr(query.table, self.field) >= self.value) + query.base_query = query.base_query.filter(field >= self.value) elif self.operation == "in": - query.base_query = query.base_query.filter(getattr(query.table, self.field).in_(self.value)) + query.base_query = query.base_query.filter(field.in_(self.value)) else: raise BadFilterError(f" Bad operation given to where filter. operation: {self.operation}") diff --git a/common/helpers.py b/common/helpers.py index a9ef36dd..6d4bca15 100644 --- a/common/helpers.py +++ b/common/helpers.py @@ -21,7 +21,7 @@ def requires_session_id(method): :param method: The method for the endpoint :returns a 403, "Forbidden" if a valid session_id is not provided with the request """ - log.info("") + @wraps(method) def wrapper_requires_session(*args, **kwargs): diff --git a/dev-requirements.in b/dev-requirements.in new file mode 100644 index 00000000..d33682d6 --- /dev/null +++ b/dev-requirements.in @@ -0,0 +1,2 @@ +Faker == 2.0.2 +pyyaml == 5.1.2 \ No newline at end of file diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 00000000..71d6db96 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,11 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile '.\dev-requirements.in' +# +faker==2.0.2 +python-dateutil==2.8.0 # via faker +pyyaml==5.1.2 +six==1.12.0 # via faker, python-dateutil +text-unidecode==1.3 # via faker diff --git a/requirements.in b/requirements.in index 17aec807..9496b805 100644 --- a/requirements.in +++ b/requirements.in @@ -1,6 +1,4 @@ flask_restful == 0.3.7 sqlalchemy == 1.3.8 pymysql == 0.9.3 -pyyaml == 5.1.2 flask-cors == 3.0.8 -Faker == 2.0.2 diff --git a/requirements.txt b/requirements.txt index 13f4fbef..ff3c4fe6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,11 +2,10 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile +# pip-compile '.\requirements.in' # aniso8601==8.0.0 # via flask-restful click==7.0 # via flask -faker==2.0.2 flask-cors==3.0.8 flask==1.1.1 # via flask-cors, flask-restful flask_restful==0.3.7 @@ -14,10 +13,7 @@ itsdangerous==1.1.0 # via flask jinja2==2.10.1 # via flask markupsafe==1.1.1 # via jinja2 pymysql==0.9.3 -python-dateutil==2.8.0 # via faker pytz==2019.2 # via flask-restful -pyyaml==5.1.2 -six==1.12.0 # via faker, flask-cors, flask-restful, python-dateutil +six==1.12.0 # via flask-cors, flask-restful sqlalchemy==1.3.8 -text-unidecode==1.3 # via faker werkzeug==0.16.0 # via flask diff --git a/src/main.py b/src/main.py index 0cfa3893..895256ab 100644 --- a/src/main.py +++ b/src/main.py @@ -42,5 +42,4 @@ "/instruments//facilitycycles//investigations/count") if __name__ == "__main__": - app.run(host=config.get_host(), port=config.get_port()) - app.run(debug=config.is_debug_mode()) + app.run(host=config.get_host(), port=config.get_port(), debug=config.is_debug_mode()