Skip to content

Commit

Permalink
addessed comments by sphuber to PR aiidateam#3429
Browse files Browse the repository at this point in the history
  • Loading branch information
elsapassaro committed Oct 17, 2019
1 parent dfd32ba commit 4e28821
Show file tree
Hide file tree
Showing 19 changed files with 107 additions and 353 deletions.
11 changes: 1 addition & 10 deletions aiida/restapi/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,6 @@ def build_translator_parameters(self, field_list):
elist = None
nelist = None
filename = None
rtype = None
download_format = None
download = True
attributes = None
Expand Down Expand Up @@ -561,8 +560,6 @@ def build_translator_parameters(self, field_list):
raise RestInputValidationError('You cannot specify download_format more than once')
if 'filename' in field_counts.keys() and field_counts['filename'] > 1:
raise RestInputValidationError('You cannot specify filename more than once')
if 'rtype' in field_counts.keys() and field_counts['rtype'] > 1:
raise RestInputValidationError('You cannot specify rtype more than once')
if 'in_limit' in field_counts.keys() and field_counts['in_limit'] > 1:
raise RestInputValidationError('You cannot specify in_limit more than once')
if 'out_limit' in field_counts.keys() and field_counts['out_limit'] > 1:
Expand Down Expand Up @@ -646,12 +643,6 @@ def build_translator_parameters(self, field_list):
else:
raise RestInputValidationError("only assignment operator '=' is permitted after 'filename'")

elif field[0] == 'rtype':
if field[1] == '=':
rtype = field[2]
else:
raise RestInputValidationError("only assignment operator '=' is permitted after 'rtype'")

elif field[0] == 'full_type':
if field[1] == '=':
full_type = field[2]
Expand Down Expand Up @@ -723,7 +714,7 @@ def build_translator_parameters(self, field_list):

return (
limit, offset, perpage, orderby, filters, alist, nalist, elist, nelist, download_format, download, filename,
rtype, tree_in_limit, tree_out_limit, attributes, attributes_filter, extras, extras_filter, full_type
tree_in_limit, tree_out_limit, attributes, attributes_filter, extras, extras_filter, full_type
)

def parse_query_string(self, query_string):
Expand Down
126 changes: 50 additions & 76 deletions aiida/restapi/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from flask import request, make_response
from flask_restful import Resource

from aiida.common.lang import classproperty
from aiida.restapi.common.exceptions import RestInputValidationError
from aiida.restapi.common.utils import Utils


Expand Down Expand Up @@ -91,22 +93,39 @@ class BaseResource(Resource):
Each derived class will instantiate a different type of translator.
This is the only difference in the classes.
"""
from aiida.restapi.translator.base import BaseTranslator

_translator_class = BaseTranslator
_parse_pk_uuid = None # Flag to tell the path parser whether to expect a pk or a uuid pattern

## TODO add the caching support. I cache total count, results, and possibly

def __init__(self, **kwargs):

self.trans = None

# Flag to tell the path parser whether to expect a pk or a uuid pattern
self.parse_pk_uuid = None
self.trans = self._translator_class(**kwargs)

# Configure utils
utils_conf_keys = ('PREFIX', 'PERPAGE_DEFAULT', 'LIMIT_DEFAULT')
self.utils_confs = {k: kwargs[k] for k in utils_conf_keys if k in kwargs}
self.utils = Utils(**self.utils_confs)
self.method_decorators = {'get': kwargs.get('get_decorators', [])}

@classproperty
def parse_pk_uuid(cls):
# pylint: disable=no-self-argument
return cls._parse_pk_uuid

def _load_and_verify(self, node_id=None):
"""Load node and verify it is of the required type"""
from aiida.orm import load_node
node = load_node(node_id)

if not isinstance(node, self.trans._aiida_class): # pylint: disable=protected-access
raise RestInputValidationError(
'node {} is not of the required type {}'.format(node_id, self.trans._aiida_class) # pylint: disable=protected-access
)

return node

def get(self, id=None, page=None): # pylint: disable=redefined-builtin,invalid-name,unused-argument
# pylint: disable=too-many-locals
"""
Expand All @@ -128,7 +147,7 @@ def get(self, id=None, page=None): # pylint: disable=redefined-builtin,invalid-
# pylint: disable=unused-variable
(
limit, offset, perpage, orderby, filters, alist, nalist, elist, nelist, download_format, download, filename,
rtype, tree_in_limit, tree_out_limit, attributes, attributes_filter, extras, extras_filter, full_type
tree_in_limit, tree_out_limit, attributes, attributes_filter, extras, extras_filter, full_type
) = self.utils.parse_query_string(query_string)

## Validate request
Expand Down Expand Up @@ -184,24 +203,21 @@ def get(self, id=None, page=None): # pylint: disable=redefined-builtin,invalid-
return self.utils.build_response(status=200, headers=headers, data=data)


class Node(Resource):
class Node(BaseResource):
"""
Differs from BaseResource in trans.set_query() mostly because it takes
query_type as an input and the presence of additional result types like "tree"
"""
from aiida.restapi.translator.nodes.node import NodeTranslator

def __init__(self, **kwargs):

# Set translator
from aiida.restapi.translator.nodes.node import NodeTranslator
self.trans = NodeTranslator(**kwargs)
_translator_class = NodeTranslator
_parse_pk_uuid = 'uuid' # Parse a uuid pattern in the URL path (not a pk)

def __init__(self, **kwargs):
super(Node, self).__init__(**kwargs)
from aiida.orm import Node as tNode
self.tclass = tNode

# Parse a uuid pattern in the URL path (not a pk)
self.parse_pk_uuid = 'uuid'

# Configure utils
utils_conf_keys = ('PREFIX', 'PERPAGE_DEFAULT', 'LIMIT_DEFAULT')
self.utils_confs = {k: kwargs[k] for k in utils_conf_keys if k in kwargs}
Expand Down Expand Up @@ -229,7 +245,7 @@ def get(self, id=None, page=None): # pylint: disable=redefined-builtin,invalid-

(
limit, offset, perpage, orderby, filters, alist, nalist, elist, nelist, download_format, download, filename,
rtype, tree_in_limit, tree_out_limit, attributes, attributes_filter, extras, extras_filter, full_type
tree_in_limit, tree_out_limit, attributes, attributes_filter, extras, extras_filter, full_type
) = self.utils.parse_query_string(query_string)

## Validate request
Expand Down Expand Up @@ -330,7 +346,6 @@ def get(self, id=None, page=None): # pylint: disable=redefined-builtin,invalid-
download_format=download_format,
download=download,
filename=filename,
rtype=rtype,
attributes=attributes,
attributes_filter=attributes_filter,
extras=extras,
Expand Down Expand Up @@ -368,23 +383,6 @@ def get(self, id=None, page=None): # pylint: disable=redefined-builtin,invalid-

results = results['download']['data']

if query_type in ['retrieved_inputs', 'retrieved_outputs'] and results:
try:
status = results[query_type]['status']
except KeyError:
status = ''
except TypeError:
status = ''

if status == 200:
data = results[query_type]['data']
response = make_response(data)
response.headers['content-type'] = 'application/octet-stream'
response.headers['Content-Disposition'] = 'attachment; filename="{}"'.format(
results[query_type]['filename']
)
return response

headers = self.utils.build_headers(url=request.url, total_count=total_count)

## Build response
Expand All @@ -404,51 +402,33 @@ def get(self, id=None, page=None): # pylint: disable=redefined-builtin,invalid-

class Computer(BaseResource):
""" Resource for Computer """
from aiida.restapi.translator.computer import ComputerTranslator

def __init__(self, **kwargs):
super(Computer, self).__init__(**kwargs)

## Instantiate the correspondent translator
from aiida.restapi.translator.computer import ComputerTranslator
self.trans = ComputerTranslator(**kwargs)

# Set wheteher to expect a pk (integer) or a uuid pattern (string) in
# the URL path
self.parse_pk_uuid = 'uuid'
_translator_class = ComputerTranslator
_parse_pk_uuid = 'uuid'


class Group(BaseResource):
""" Resource for Group """
from aiida.restapi.translator.group import GroupTranslator

def __init__(self, **kwargs):
super(Group, self).__init__(**kwargs)

from aiida.restapi.translator.group import GroupTranslator
self.trans = GroupTranslator(**kwargs)

self.parse_pk_uuid = 'uuid'
_translator_class = GroupTranslator
_parse_pk_uuid = 'uuid'


class User(BaseResource):
""" Resource for User """
from aiida.restapi.translator.user import UserTranslator

def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)

from aiida.restapi.translator.user import UserTranslator
self.trans = UserTranslator(**kwargs)

self.parse_pk_uuid = 'pk'
_translator_class = UserTranslator
_parse_pk_uuid = 'pk'


class ProcessNode(Node):
""" Resource for ProcessNode """
from aiida.restapi.translator.nodes.process.process import ProcessTranslator

def __init__(self, **kwargs):
super(ProcessNode, self).__init__(**kwargs)

from aiida.restapi.translator.nodes.process.process import ProcessTranslator
self.trans = ProcessTranslator(**kwargs)
_translator_class = ProcessTranslator

def get(self, id=None, page=None): # pylint: disable=redefined-builtin
"""
Expand All @@ -468,9 +448,8 @@ def get(self, id=None, page=None): # pylint: disable=redefined-builtin

results = None
if query_type == 'report':
from aiida.orm import load_node
node_obj = load_node(node_id)
report = self.trans.get_report(node_obj)
node = self._load_and_verify(node_id)
report = self.trans.get_report(node)
results = report

elif query_type == 'projectable_properties':
Expand Down Expand Up @@ -498,12 +477,9 @@ def get(self, id=None, page=None): # pylint: disable=redefined-builtin

class CalcJobNode(ProcessNode):
""" Resource for CalcJobNode """
from aiida.restapi.translator.nodes.process.calculation.calcjob import CalcJobTranslator

def __init__(self, **kwargs):
super(CalcJobNode, self).__init__(**kwargs)

from aiida.restapi.translator.nodes.process.calculation.calcjob import CalcJobTranslator
self.trans = CalcJobTranslator(**kwargs)
_translator_class = CalcJobTranslator

def get(self, id=None, page=None): # pylint: disable=redefined-builtin
"""
Expand All @@ -521,18 +497,16 @@ def get(self, id=None, page=None): # pylint: disable=redefined-builtin
## Parse request
(resource_type, page, node_id, query_type) = self.utils.parse_path(path, parse_pk_uuid=self.parse_pk_uuid)

node = self._load_and_verify(node_id)
results = None

params = request.args
filename = params.get('filename', '')

from aiida.orm import load_node
node_obj = load_node(node_id)

if query_type == 'input_files':
results = self.trans.get_input_files(node_obj, filename)
results = self.trans.get_input_files(node, filename)
elif query_type == 'output_files':
results = self.trans.get_output_files(node_obj, filename)
results = self.trans.get_output_files(node, filename)

## Build response and return it
headers = self.utils.build_headers(url=request.url, total_count=1)
Expand Down
21 changes: 1 addition & 20 deletions aiida/restapi/translator/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class BaseTranslator(object):
_is_id_query = None
_total_count = None

def __init__(self, Class=None, **kwargs):
def __init__(self, **kwargs):
"""
Initialise the parameters.
Create the basic query_help
Expand All @@ -58,24 +58,6 @@ def __init__(self, Class=None, **kwargs):
attributes. In case of inheritance one cane use the
same constructore but pass the inheriting class to pass its attributes.
"""

# Assume default class is this class (cannot be done in the
# definition as it requires self)
if Class is None:
Class = self.__class__

# Assign class parameters to the object
self.__label__ = Class.__label__
self._aiida_class = Class._aiida_class # pylint: disable=protected-access
self._aiida_type = Class._aiida_type # pylint: disable=protected-access
self._result_type = Class.__label__

self._default = Class._default # pylint: disable=protected-access
self._default_projections = Class._default_projections # pylint: disable=protected-access
self._is_qb_initialized = Class._is_qb_initialized # pylint: disable=protected-access
self._is_id_query = Class._is_id_query # pylint: disable=protected-access
self._total_count = Class._total_count # pylint: disable=protected-access

# Basic filter (dict) to set the identity of the uuid. None if
# no specific node is requested
self._id_filter = None
Expand Down Expand Up @@ -310,7 +292,6 @@ def set_query(
:param elist: list of extras queries for node
:param nelist: list of extras, returns all extras except this for node
:param filename: name of the file to return its content
:param rtype: return type of the file
:param attributes: flag to show attributes in nodes endpoint
:param attributes_filter: list of node attributes to query
:param extras: flag to show attributes in nodes endpoint
Expand Down
Loading

0 comments on commit 4e28821

Please sign in to comment.