diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..cdd823e64e7e91ae85da84f22410ecb7eb370ae2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,72 @@ +.travis.yaml +.swagger-codegen-ignore +README.md +tox.ini +git_push.sh +test-requirements.txt +setup.py + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ +venv/ +.python-version + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +#Ipython Notebook +.ipynb_checkpoints diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a655050c2631466828b5b8bfc59ae27f9ac02dc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ +venv/ +.python-version + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +#Ipython Notebook +.ipynb_checkpoints diff --git a/.swagger-codegen-ignore b/.swagger-codegen-ignore new file mode 100644 index 0000000000000000000000000000000000000000..c5fa491b4c557bf997d5dd21797de782545dc9e5 --- /dev/null +++ b/.swagger-codegen-ignore @@ -0,0 +1,23 @@ +# Swagger Codegen Ignore +# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/.swagger-codegen/VERSION b/.swagger-codegen/VERSION new file mode 100644 index 0000000000000000000000000000000000000000..752a79ef362d2555ab0957f30657c0d6616b6cb5 --- /dev/null +++ b/.swagger-codegen/VERSION @@ -0,0 +1 @@ +2.4.8 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..dd6c4450aa902ae68479c3d76d45145e18d6052e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +# ref: https://docs.travis-ci.com/user/languages/python +language: python +python: + - "3.2" + - "3.3" + - "3.4" + - "3.5" + #- "3.5-dev" # 3.5 development branch + #- "nightly" # points to the latest development branch e.g. 3.6-dev +# command to install dependencies +install: "pip install -r requirements.txt" +# command to run tests +script: nosetests diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..e879fcc560ce44570318342b7c965539b1f10db9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3-alpine + +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app + +COPY requirements.txt /usr/src/app/ + +RUN pip3 install --no-cache-dir -r requirements.txt + +COPY . /usr/src/app + +EXPOSE 8080 + +ENTRYPOINT ["python3"] + +CMD ["-m", "access_node"] \ No newline at end of file diff --git a/README.md b/README.md index 517e27d0042eaa0b17343e25538037c38174b252..211196b339dd726ad09f886180d4e648b4a3ae88 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,52 @@ # access-node +Public access server providing a REST API for the in-situ pipeline. -Public access server providing REST API for the in-situ pipeline. \ No newline at end of file +# Swagger generated server + +## Overview +This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the +[OpenAPI-Spec](https://github.com/swagger-api/swagger-core/wiki) from a remote server, you can easily generate a server stub. This +is an example of building a swagger-enabled Flask server. + +This example uses the [Connexion](https://github.com/zalando/connexion) library on top of Flask. + +## Requirements +Python 3.5.2+ + +## Usage +To run the server, please execute the following from the root directory: + +``` +pip3 install -r requirements.txt +python3 -m access_node +``` + +and open your browser to here: + +``` +http://localhost:8080//ui/ +``` + +Your Swagger definition lives here: + +``` +http://localhost:8080//swagger.json +``` + +To launch the integration tests, use tox: +``` +sudo pip install tox +tox +``` + +## Running with Docker + +To run the server on a Docker container, please execute the following from the root directory: + +```bash +# building the image +docker build -t access_node . + +# starting up a container +docker run -p 8080:8080 access_node +``` diff --git a/access_node/__init__.py b/access_node/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/access_node/__main__.py b/access_node/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..d96cc1630eae64e8bace757db08bebe2bb3c67b3 --- /dev/null +++ b/access_node/__main__.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +import connexion + +from access_node import encoder + + +def main(): + app = connexion.App(__name__, specification_dir='./swagger/') + app.app.json_encoder = encoder.JSONEncoder + app.add_api('swagger.yaml', arguments={'title': 'In-Situ Pipeline REST API'}) + app.run(port=8080) + + +if __name__ == '__main__': + main() diff --git a/access_node/controllers/__init__.py b/access_node/controllers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/access_node/controllers/nest_controller.py b/access_node/controllers/nest_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..0a0f5c21ba6cca139a6671249d7969760bdd87b2 --- /dev/null +++ b/access_node/controllers/nest_controller.py @@ -0,0 +1,84 @@ +import connexion +import six + +from access_node.models.attribute import Attribute # noqa: E501 +from access_node.models.data import Data # noqa: E501 +from access_node.models.error import Error # noqa: E501 +from access_node.models.spikes import Spikes # noqa: E501 +from access_node import util + + +def get_attributes(): # noqa: E501 + """Retrieves the details of per-neuron attributes. + + # noqa: E501 + + + :rtype: List[Attribute] + """ + return 'do some magic!' + + +def get_data(attribute, simulation_steps=None, neuron_ids=None): # noqa: E501 + """Retrieves the per-neuron attributes for the given attribute name, simulation steps (optional) and neuron IDs (optional). + + # noqa: E501 + + :param attribute: Attribute name. + :type attribute: str + :param simulation_steps: Simulation steps (leave empty for all steps). + :type simulation_steps: List[float] + :param neuron_ids: Neuron IDs (leave empty for all neurons). + :type neuron_ids: List[] + + :rtype: Data + """ + return 'do some magic!' + + +def get_neuron_ids(): # noqa: E501 + """Retrieves the list of all neuron IDs. + + # noqa: E501 + + + :rtype: List[float] + """ + return 'do some magic!' + + +def get_simulation_step_count(): # noqa: E501 + """Retrieves the number of simulation steps. + + # noqa: E501 + + + :rtype: float + """ + return 'do some magic!' + + +def get_spikes(simulation_steps=None, neuron_ids=None): # noqa: E501 + """Retrieves the spikes for the given simulation steps (optional) and neuron IDs (optional). + + # noqa: E501 + + :param simulation_steps: Simulation steps (leave empty for all steps). + :type simulation_steps: List[] + :param neuron_ids: Neuron IDs (leave empty for all neurons). + :type neuron_ids: List[] + + :rtype: Spikes + """ + return 'do some magic!' + + +def get_timestep(): # noqa: E501 + """Retrieves the time between two simulation steps. + + # noqa: E501 + + + :rtype: float + """ + return 'do some magic!' diff --git a/access_node/encoder.py b/access_node/encoder.py new file mode 100644 index 0000000000000000000000000000000000000000..71d15753866cd1e1018dccce4a70c4c0336ba008 --- /dev/null +++ b/access_node/encoder.py @@ -0,0 +1,20 @@ +from connexion.apps.flask_app import FlaskJSONEncoder +import six + +from access_node.models.base_model_ import Model + + +class JSONEncoder(FlaskJSONEncoder): + include_nulls = False + + def default(self, o): + if isinstance(o, Model): + dikt = {} + for attr, _ in six.iteritems(o.swagger_types): + value = getattr(o, attr) + if value is None and not self.include_nulls: + continue + attr = o.attribute_map[attr] + dikt[attr] = value + return dikt + return FlaskJSONEncoder.default(self, o) diff --git a/access_node/models/__init__.py b/access_node/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6102b118485d89364cfc3cd127110498f6bcbd65 --- /dev/null +++ b/access_node/models/__init__.py @@ -0,0 +1,10 @@ +# coding: utf-8 + +# flake8: noqa +from __future__ import absolute_import +# import models into model package +from access_node.models.attribute import Attribute +from access_node.models.data import Data +from access_node.models.error import Error +from access_node.models.precision import Precision +from access_node.models.spikes import Spikes diff --git a/access_node/models/attribute.py b/access_node/models/attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..8270b5a50819005149825b84dbd40f209e4b6627 --- /dev/null +++ b/access_node/models/attribute.py @@ -0,0 +1,91 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from access_node.models.base_model_ import Model +from access_node.models.precision import Precision # noqa: F401,E501 +from access_node import util + + +class Attribute(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + def __init__(self, name: str=None, precision: Precision=None): # noqa: E501 + """Attribute - a model defined in Swagger + + :param name: The name of this Attribute. # noqa: E501 + :type name: str + :param precision: The precision of this Attribute. # noqa: E501 + :type precision: Precision + """ + self.swagger_types = { + 'name': str, + 'precision': Precision + } + + self.attribute_map = { + 'name': 'name', + 'precision': 'precision' + } + + self._name = name + self._precision = precision + + @classmethod + def from_dict(cls, dikt) -> 'Attribute': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The Attribute of this Attribute. # noqa: E501 + :rtype: Attribute + """ + return util.deserialize_model(dikt, cls) + + @property + def name(self) -> str: + """Gets the name of this Attribute. + + + :return: The name of this Attribute. + :rtype: str + """ + return self._name + + @name.setter + def name(self, name: str): + """Sets the name of this Attribute. + + + :param name: The name of this Attribute. + :type name: str + """ + + self._name = name + + @property + def precision(self) -> Precision: + """Gets the precision of this Attribute. + + + :return: The precision of this Attribute. + :rtype: Precision + """ + return self._precision + + @precision.setter + def precision(self, precision: Precision): + """Sets the precision of this Attribute. + + + :param precision: The precision of this Attribute. + :type precision: Precision + """ + + self._precision = precision diff --git a/access_node/models/base_model_.py b/access_node/models/base_model_.py new file mode 100644 index 0000000000000000000000000000000000000000..02cefc7a06f66b8a91f34f17bed2b5ec7c3e4448 --- /dev/null +++ b/access_node/models/base_model_.py @@ -0,0 +1,69 @@ +import pprint + +import six +import typing + +from access_node import util + +T = typing.TypeVar('T') + + +class Model(object): + # swaggerTypes: The key is attribute name and the + # value is attribute type. + swagger_types = {} + + # attributeMap: The key is attribute name and the + # value is json key in definition. + attribute_map = {} + + @classmethod + def from_dict(cls: typing.Type[T], dikt) -> T: + """Returns the dict as a model""" + return util.deserialize_model(dikt, cls) + + def to_dict(self): + """Returns the model properties as a dict + + :rtype: dict + """ + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + + return result + + def to_str(self): + """Returns the string representation of the model + + :rtype: str + """ + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/access_node/models/data.py b/access_node/models/data.py new file mode 100644 index 0000000000000000000000000000000000000000..80a6f15b89f579931a1b2d78704cf063d815c0b5 --- /dev/null +++ b/access_node/models/data.py @@ -0,0 +1,116 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from access_node.models.base_model_ import Model +from access_node import util + + +class Data(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + def __init__(self, simulation_steps: List[float]=None, neuron_ids: List[float]=None, data: List[float]=None): # noqa: E501 + """Data - a model defined in Swagger + + :param simulation_steps: The simulation_steps of this Data. # noqa: E501 + :type simulation_steps: List[float] + :param neuron_ids: The neuron_ids of this Data. # noqa: E501 + :type neuron_ids: List[float] + :param data: The data of this Data. # noqa: E501 + :type data: List[float] + """ + self.swagger_types = { + 'simulation_steps': List[float], + 'neuron_ids': List[float], + 'data': List[float] + } + + self.attribute_map = { + 'simulation_steps': 'simulation_steps', + 'neuron_ids': 'neuron_ids', + 'data': 'data' + } + + self._simulation_steps = simulation_steps + self._neuron_ids = neuron_ids + self._data = data + + @classmethod + def from_dict(cls, dikt) -> 'Data': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The Data of this Data. # noqa: E501 + :rtype: Data + """ + return util.deserialize_model(dikt, cls) + + @property + def simulation_steps(self) -> List[float]: + """Gets the simulation_steps of this Data. + + + :return: The simulation_steps of this Data. + :rtype: List[float] + """ + return self._simulation_steps + + @simulation_steps.setter + def simulation_steps(self, simulation_steps: List[float]): + """Sets the simulation_steps of this Data. + + + :param simulation_steps: The simulation_steps of this Data. + :type simulation_steps: List[float] + """ + + self._simulation_steps = simulation_steps + + @property + def neuron_ids(self) -> List[float]: + """Gets the neuron_ids of this Data. + + + :return: The neuron_ids of this Data. + :rtype: List[float] + """ + return self._neuron_ids + + @neuron_ids.setter + def neuron_ids(self, neuron_ids: List[float]): + """Sets the neuron_ids of this Data. + + + :param neuron_ids: The neuron_ids of this Data. + :type neuron_ids: List[float] + """ + + self._neuron_ids = neuron_ids + + @property + def data(self) -> List[float]: + """Gets the data of this Data. + + + :return: The data of this Data. + :rtype: List[float] + """ + return self._data + + @data.setter + def data(self, data: List[float]): + """Sets the data of this Data. + + + :param data: The data of this Data. + :type data: List[float] + """ + + self._data = data diff --git a/access_node/models/error.py b/access_node/models/error.py new file mode 100644 index 0000000000000000000000000000000000000000..563f5979be751e7eb69da0aea3a4c888e8ae01bf --- /dev/null +++ b/access_node/models/error.py @@ -0,0 +1,90 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from access_node.models.base_model_ import Model +from access_node import util + + +class Error(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + def __init__(self, code: float=None, message: str=None): # noqa: E501 + """Error - a model defined in Swagger + + :param code: The code of this Error. # noqa: E501 + :type code: float + :param message: The message of this Error. # noqa: E501 + :type message: str + """ + self.swagger_types = { + 'code': float, + 'message': str + } + + self.attribute_map = { + 'code': 'code', + 'message': 'message' + } + + self._code = code + self._message = message + + @classmethod + def from_dict(cls, dikt) -> 'Error': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The Error of this Error. # noqa: E501 + :rtype: Error + """ + return util.deserialize_model(dikt, cls) + + @property + def code(self) -> float: + """Gets the code of this Error. + + + :return: The code of this Error. + :rtype: float + """ + return self._code + + @code.setter + def code(self, code: float): + """Sets the code of this Error. + + + :param code: The code of this Error. + :type code: float + """ + + self._code = code + + @property + def message(self) -> str: + """Gets the message of this Error. + + + :return: The message of this Error. + :rtype: str + """ + return self._message + + @message.setter + def message(self, message: str): + """Sets the message of this Error. + + + :param message: The message of this Error. + :type message: str + """ + + self._message = message diff --git a/access_node/models/precision.py b/access_node/models/precision.py new file mode 100644 index 0000000000000000000000000000000000000000..5d4b1e389c11f6a5ab7d7ff3a211c7fffd02c415 --- /dev/null +++ b/access_node/models/precision.py @@ -0,0 +1,43 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from access_node.models.base_model_ import Model +from access_node import util + + +class Precision(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + allowed enum values + """ + DOUBLE = "double" + INTEGER = "integer" + + def __init__(self): # noqa: E501 + """Precision - a model defined in Swagger + + """ + self.swagger_types = { + } + + self.attribute_map = { + } + + @classmethod + def from_dict(cls, dikt) -> 'Precision': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The Precision of this Precision. # noqa: E501 + :rtype: Precision + """ + return util.deserialize_model(dikt, cls) diff --git a/access_node/models/spikes.py b/access_node/models/spikes.py new file mode 100644 index 0000000000000000000000000000000000000000..d63abc26cd103bd04e74dde0726c0511d982fe4b --- /dev/null +++ b/access_node/models/spikes.py @@ -0,0 +1,90 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from access_node.models.base_model_ import Model +from access_node import util + + +class Spikes(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + def __init__(self, simulation_steps: List[float]=None, neuron_ids: List[float]=None): # noqa: E501 + """Spikes - a model defined in Swagger + + :param simulation_steps: The simulation_steps of this Spikes. # noqa: E501 + :type simulation_steps: List[float] + :param neuron_ids: The neuron_ids of this Spikes. # noqa: E501 + :type neuron_ids: List[float] + """ + self.swagger_types = { + 'simulation_steps': List[float], + 'neuron_ids': List[float] + } + + self.attribute_map = { + 'simulation_steps': 'simulation_steps', + 'neuron_ids': 'neuron_ids' + } + + self._simulation_steps = simulation_steps + self._neuron_ids = neuron_ids + + @classmethod + def from_dict(cls, dikt) -> 'Spikes': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The Spikes of this Spikes. # noqa: E501 + :rtype: Spikes + """ + return util.deserialize_model(dikt, cls) + + @property + def simulation_steps(self) -> List[float]: + """Gets the simulation_steps of this Spikes. + + + :return: The simulation_steps of this Spikes. + :rtype: List[float] + """ + return self._simulation_steps + + @simulation_steps.setter + def simulation_steps(self, simulation_steps: List[float]): + """Sets the simulation_steps of this Spikes. + + + :param simulation_steps: The simulation_steps of this Spikes. + :type simulation_steps: List[float] + """ + + self._simulation_steps = simulation_steps + + @property + def neuron_ids(self) -> List[float]: + """Gets the neuron_ids of this Spikes. + + + :return: The neuron_ids of this Spikes. + :rtype: List[float] + """ + return self._neuron_ids + + @neuron_ids.setter + def neuron_ids(self, neuron_ids: List[float]): + """Sets the neuron_ids of this Spikes. + + + :param neuron_ids: The neuron_ids of this Spikes. + :type neuron_ids: List[float] + """ + + self._neuron_ids = neuron_ids diff --git a/access_node/swagger/swagger.yaml b/access_node/swagger/swagger.yaml new file mode 100644 index 0000000000000000000000000000000000000000..783410322e790d50865d69954a728ac5faf4cf9e --- /dev/null +++ b/access_node/swagger/swagger.yaml @@ -0,0 +1,250 @@ +--- +swagger: "2.0" +info: + description: "This is the REST API for the in-situ pipeline." + version: "1.0.0" + title: "In-Situ Pipeline REST API" +host: "localhost" +basePath: "/" +schemes: +- "https" +- "http" +paths: + /timestep: + get: + tags: + - "nest" + summary: "Retrieves the time between two simulation steps." + operationId: "get_timestep" + produces: + - "application/json" + parameters: [] + responses: + 200: + description: "Operation successful." + schema: + type: "number" + format: "double" + 400: + description: "Operation failed." + schema: + $ref: "#/definitions/Error" + x-swagger-router-controller: "swagger_server.controllers.nest_controller" + /simulation_step_count: + get: + tags: + - "nest" + summary: "Retrieves the number of simulation steps." + operationId: "get_simulation_step_count" + produces: + - "application/json" + parameters: [] + responses: + 200: + description: "Operation successful." + schema: + type: "number" + format: "int64" + 400: + description: "Operation failed." + schema: + $ref: "#/definitions/Error" + x-swagger-router-controller: "swagger_server.controllers.nest_controller" + /neuron_ids: + get: + tags: + - "nest" + summary: "Retrieves the list of all neuron IDs." + operationId: "get_neuron_ids" + produces: + - "application/json" + parameters: [] + responses: + 200: + description: "Operation successful." + schema: + type: "array" + items: + type: "number" + format: "int64" + 400: + description: "Operation failed." + schema: + $ref: "#/definitions/Error" + x-swagger-router-controller: "swagger_server.controllers.nest_controller" + /attributes: + get: + tags: + - "nest" + summary: "Retrieves the details of per-neuron attributes." + operationId: "get_attributes" + produces: + - "application/json" + parameters: [] + responses: + 200: + description: "Operation successful." + schema: + type: "array" + items: + $ref: "#/definitions/Attribute" + 400: + description: "Operation failed." + schema: + $ref: "#/definitions/Error" + x-swagger-router-controller: "swagger_server.controllers.nest_controller" + /data: + get: + tags: + - "nest" + summary: "Retrieves the per-neuron attributes for the given attribute name,\ + \ simulation steps (optional) and neuron IDs (optional)." + operationId: "get_data" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "attribute" + in: "query" + description: "Attribute name." + required: true + type: "string" + - name: "simulation_steps" + in: "query" + description: "Simulation steps (leave empty for all steps)." + required: false + type: "array" + items: + type: "number" + format: "double" + collectionFormat: "multi" + - name: "neuron_ids" + in: "query" + description: "Neuron IDs (leave empty for all neurons)." + required: false + type: "array" + items: + type: "number" + format: "int64" + collectionFormat: "multi" + responses: + 200: + description: "Operation successful." + schema: + $ref: "#/definitions/Data" + 400: + description: "Operation failed." + schema: + $ref: "#/definitions/Error" + x-swagger-router-controller: "swagger_server.controllers.nest_controller" + /spikes: + get: + tags: + - "nest" + summary: "Retrieves the spikes for the given simulation steps (optional) and\ + \ neuron IDs (optional)." + operationId: "get_spikes" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "simulation_steps" + in: "query" + description: "Simulation steps (leave empty for all steps)." + required: false + type: "array" + items: + type: "number" + format: "int64" + collectionFormat: "multi" + - name: "neuron_ids" + in: "query" + description: "Neuron IDs (leave empty for all neurons)." + required: false + type: "array" + items: + type: "number" + format: "int64" + collectionFormat: "multi" + responses: + 200: + description: "Operation successful." + schema: + $ref: "#/definitions/Spikes" + 400: + description: "Operation failed." + schema: + $ref: "#/definitions/Error" + x-swagger-router-controller: "swagger_server.controllers.nest_controller" +definitions: + Precision: + type: "string" + enum: + - "double" + - "integer" + Attribute: + type: "object" + properties: + name: + type: "string" + precision: + $ref: "#/definitions/Precision" + example: + precision: {} + name: "name" + Data: + type: "object" + properties: + simulation_steps: + type: "array" + items: + type: "number" + format: "int64" + neuron_ids: + type: "array" + items: + type: "number" + format: "int64" + data: + type: "array" + items: + type: "number" + example: + simulation_steps: + - 0.8008281904610115 + - 0.8008281904610115 + data: + - 1.4658129805029452 + - 1.4658129805029452 + neuron_ids: + - 6.027456183070403 + - 6.027456183070403 + Spikes: + type: "object" + properties: + simulation_steps: + type: "array" + items: + type: "number" + format: "int64" + neuron_ids: + type: "array" + items: + type: "number" + format: "int64" + example: + simulation_steps: + - 0.8008281904610115 + - 0.8008281904610115 + neuron_ids: + - 6.027456183070403 + - 6.027456183070403 + Error: + type: "object" + properties: + code: + type: "number" + message: + type: "string" diff --git a/access_node/test/__init__.py b/access_node/test/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..714483d1cc1005b80367398e35b7a662f25766e1 --- /dev/null +++ b/access_node/test/__init__.py @@ -0,0 +1,16 @@ +import logging + +import connexion +from flask_testing import TestCase + +from access_node.encoder import JSONEncoder + + +class BaseTestCase(TestCase): + + def create_app(self): + logging.getLogger('connexion.operation').setLevel('ERROR') + app = connexion.App(__name__, specification_dir='../swagger/') + app.app.json_encoder = JSONEncoder + app.add_api('swagger.yaml') + return app.app diff --git a/access_node/test/test_nest_controller.py b/access_node/test/test_nest_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..8bb3f90898f143701998b3ead405f70d1e539c98 --- /dev/null +++ b/access_node/test/test_nest_controller.py @@ -0,0 +1,96 @@ +# coding: utf-8 + +from __future__ import absolute_import + +from flask import json +from six import BytesIO + +from access_node.models.attribute import Attribute # noqa: E501 +from access_node.models.data import Data # noqa: E501 +from access_node.models.error import Error # noqa: E501 +from access_node.models.spikes import Spikes # noqa: E501 +from access_node.test import BaseTestCase + + +class TestNestController(BaseTestCase): + """NestController integration test stubs""" + + def test_get_attributes(self): + """Test case for get_attributes + + Retrieves the details of per-neuron attributes. + """ + response = self.client.open( + '//attributes', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_get_data(self): + """Test case for get_data + + Retrieves the per-neuron attributes for the given attribute name, simulation steps (optional) and neuron IDs (optional). + """ + query_string = [('attribute', 'attribute_example'), + ('simulation_steps', 3.4), + ('neuron_ids', 3.4)] + response = self.client.open( + '//data', + method='GET', + content_type='application/json', + query_string=query_string) + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_get_neuron_ids(self): + """Test case for get_neuron_ids + + Retrieves the list of all neuron IDs. + """ + response = self.client.open( + '//neuron_ids', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_get_simulation_step_count(self): + """Test case for get_simulation_step_count + + Retrieves the number of simulation steps. + """ + response = self.client.open( + '//simulation_step_count', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_get_spikes(self): + """Test case for get_spikes + + Retrieves the spikes for the given simulation steps (optional) and neuron IDs (optional). + """ + query_string = [('simulation_steps', 3.4), + ('neuron_ids', 3.4)] + response = self.client.open( + '//spikes', + method='GET', + content_type='application/json', + query_string=query_string) + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_get_timestep(self): + """Test case for get_timestep + + Retrieves the time between two simulation steps. + """ + response = self.client.open( + '//timestep', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/access_node/util.py b/access_node/util.py new file mode 100644 index 0000000000000000000000000000000000000000..527d1424c3d5563073b18b4da1a9904b2cb6b62b --- /dev/null +++ b/access_node/util.py @@ -0,0 +1,141 @@ +import datetime + +import six +import typing + + +def _deserialize(data, klass): + """Deserializes dict, list, str into an object. + + :param data: dict, list or str. + :param klass: class literal, or string of class name. + + :return: object. + """ + if data is None: + return None + + if klass in six.integer_types or klass in (float, str, bool): + return _deserialize_primitive(data, klass) + elif klass == object: + return _deserialize_object(data) + elif klass == datetime.date: + return deserialize_date(data) + elif klass == datetime.datetime: + return deserialize_datetime(data) + elif type(klass) == typing.GenericMeta: + if klass.__extra__ == list: + return _deserialize_list(data, klass.__args__[0]) + if klass.__extra__ == dict: + return _deserialize_dict(data, klass.__args__[1]) + else: + return deserialize_model(data, klass) + + +def _deserialize_primitive(data, klass): + """Deserializes to primitive type. + + :param data: data to deserialize. + :param klass: class literal. + + :return: int, long, float, str, bool. + :rtype: int | long | float | str | bool + """ + try: + value = klass(data) + except UnicodeEncodeError: + value = six.u(data) + except TypeError: + value = data + return value + + +def _deserialize_object(value): + """Return a original value. + + :return: object. + """ + return value + + +def deserialize_date(string): + """Deserializes string to date. + + :param string: str. + :type string: str + :return: date. + :rtype: date + """ + try: + from dateutil.parser import parse + return parse(string).date() + except ImportError: + return string + + +def deserialize_datetime(string): + """Deserializes string to datetime. + + The string should be in iso8601 datetime format. + + :param string: str. + :type string: str + :return: datetime. + :rtype: datetime + """ + try: + from dateutil.parser import parse + return parse(string) + except ImportError: + return string + + +def deserialize_model(data, klass): + """Deserializes list or dict to model. + + :param data: dict, list. + :type data: dict | list + :param klass: class literal. + :return: model object. + """ + instance = klass() + + if not instance.swagger_types: + return data + + for attr, attr_type in six.iteritems(instance.swagger_types): + if data is not None \ + and instance.attribute_map[attr] in data \ + and isinstance(data, (list, dict)): + value = data[instance.attribute_map[attr]] + setattr(instance, attr, _deserialize(value, attr_type)) + + return instance + + +def _deserialize_list(data, boxed_type): + """Deserializes a list and its elements. + + :param data: list to deserialize. + :type data: list + :param boxed_type: class literal. + + :return: deserialized list. + :rtype: list + """ + return [_deserialize(sub_data, boxed_type) + for sub_data in data] + + +def _deserialize_dict(data, boxed_type): + """Deserializes a dict and its elements. + + :param data: dict to deserialize. + :type data: dict + :param boxed_type: class literal. + + :return: deserialized dict. + :rtype: dict + """ + return {k: _deserialize(v, boxed_type) + for k, v in six.iteritems(data)} diff --git a/config.json b/config.json new file mode 100644 index 0000000000000000000000000000000000000000..47123716f74172d9703de62bd7e87d2a60f28283 --- /dev/null +++ b/config.json @@ -0,0 +1,3 @@ +{ + "packageName":"access_node" +} \ No newline at end of file diff --git a/git_push.sh b/git_push.sh new file mode 100644 index 0000000000000000000000000000000000000000..160f6f213999c7ae67839fbd6fc2ba0b72675832 --- /dev/null +++ b/git_push.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..1cf2462510b188810ea421de672761edb3d92f08 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +connexion == 1.1.15 +python_dateutil == 2.6.0 +setuptools >= 21.0.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..166d5893ad2541bbeab42b4cc348d0c6d63356a4 --- /dev/null +++ b/setup.py @@ -0,0 +1,35 @@ +# coding: utf-8 + +import sys +from setuptools import setup, find_packages + +NAME = "access_node" +VERSION = "1.0.0" + +# To install the library, run the following +# +# python setup.py install +# +# prerequisite: setuptools +# http://pypi.python.org/pypi/setuptools + +REQUIRES = ["connexion"] + +setup( + name=NAME, + version=VERSION, + description="In-Situ Pipeline REST API", + author_email="", + url="", + keywords=["Swagger", "In-Situ Pipeline REST API"], + install_requires=REQUIRES, + packages=find_packages(), + package_data={'': ['swagger/swagger.yaml']}, + include_package_data=True, + entry_points={ + 'console_scripts': ['access_node=access_node.__main__:main']}, + long_description="""\ + This is the REST API for the in-situ pipeline. + """ +) + diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..7f8d96e6b40e853d658f88fa745ddcf37ff7644c --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,6 @@ +flask_testing==0.6.1 +coverage>=4.0.3 +nose>=1.3.7 +pluggy>=0.3.1 +py>=1.4.31 +randomize>=0.13 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000000000000000000000000000000000000..3e0b644eec48f0d6d63f028c28315ca0eda5896f --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py35 + +[testenv] +deps=-r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +commands= + nosetests \ + [] \ No newline at end of file