diff --git a/Dockerfile b/Dockerfile index e879fcc560ce44570318342b7c965539b1f10db9..25a520c95ebb80aad13c696963ce55f5649bfad9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3-alpine +FROM python:3.6-slim RUN mkdir -p /usr/src/app WORKDIR /usr/src/app diff --git a/README.md b/README.md index 6b54a5ee028b52d2f4505c5c36e7aaa15f8c72ba..b276b37c9e62518e3224a3702efd253fb9f371b3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Public access server providing a REST API for the in-situ pipeline. To generate execute this from the projects root dir: ``` -java -jar <path/to/swagger-codegen-cli.2.4.8.jar> generate -i access_node/swagger/swagger.yaml -l python-flask -c config.json +java -jar swagger-codegen-cli-2.4.8.jar generate -i access_node/swagger/swagger.yaml -l python-flask -c config.json ``` # Swagger generated server diff --git a/access_node/__main__.py b/access_node/__main__.py index 8edc7f3329a1e4aed073501e3213618069c84e74..b701c9fd1784a4b2b5746d6d2637d087e4d12fe1 100644 --- a/access_node/__main__.py +++ b/access_node/__main__.py @@ -8,32 +8,144 @@ from flask_cors import CORS from access_node.models.nodes import nodes import json -import requests import time +import requests +import psycopg2 + +def ConnectToDatabase(postgres_username, postgres_password, port): + return psycopg2.connect(database="postgres", user=postgres_username, + password=postgres_password, host="database", port=str(port)) + +def SetupNestTables(postgres_username, postgres_password, port): + con = ConnectToDatabase(postgres_username, postgres_password, port) + print("Database connection opened successfully!") + + cur = con.cursor() + cur.execute("DROP TABLE IF EXISTS nest_simulation_node CASCADE") + cur.execute("DROP TABLE IF EXISTS nest_multimeter CASCADE") + cur.execute("DROP TABLE IF EXISTS nest_neuron CASCADE") + cur.execute("DROP TABLE IF EXISTS nest_neuron_multimeter CASCADE") + + + cur.execute(''' + CREATE TABLE nest_simulation_node ( + id SERIAL PRIMARY KEY NOT NULL UNIQUE, + address VARCHAR(50), + current_simulation_time FLOAT + );''') + + cur.execute(''' + CREATE TABLE nest_multimeter ( + id INT PRIMARY KEY NOT NULL UNIQUE, + attributes VARCHAR(50) ARRAY + );''') + + cur.execute(''' + CREATE TABLE nest_neuron ( + id INT PRIMARY KEY NOT NULL UNIQUE, + simulation_node_id INT, + population_id INT, + position FLOAT[], + FOREIGN KEY (simulation_node_id) REFERENCES nest_simulation_node (id) + );''') + + cur.execute(''' + CREATE TABLE nest_neuron_multimeter ( + neuron_id INT NOT NULL, + multimeter_id INT NOT NULL, + PRIMARY KEY (neuron_id,multimeter_id), + FOREIGN KEY (neuron_id) REFERENCES nest_neuron (id), + FOREIGN KEY (multimeter_id) REFERENCES nest_multimeter (id) + );''') + + con.commit() + con.close() + print("Nest tables created successfully!\n") + +def SetupArborTables(postgres_username, postgres_password, port): + con = ConnectToDatabase(postgres_username, postgres_password, port) + print("Database connection opened successfully!") + cur = con.cursor() + cur.execute("DROP TABLE IF EXISTS arbor_probe CASCADE") + cur.execute("DROP TABLE IF EXISTS arbor_cell CASCADE") + cur.execute("DROP TABLE IF EXISTS arbor_simulation_node CASCADE") + cur.execute("DROP TABLE IF EXISTS arbor_attribute CASCADE") + + cur.execute(''' + CREATE TABLE arbor_simulation_node ( + id INT PRIMARY KEY NOT NULL UNIQUE, + address VARCHAR(50), + current_simulation_time FLOAT + );''') + + cur.execute(''' + CREATE TABLE arbor_cell ( + id INT PRIMARY KEY NOT NULL UNIQUE + ); + ''') + + cur.execute(''' + CREATE TABLE cell_property ( + cell_id INT NOT NULL, + property VARCHAR(50) NOT NULL, + PRIMARY KEY (cell_id, property), + FOREIGN KEY (cell_id) REFERENCES arbor_cell (id), + );''') + + cur.execute(''' + CREATE TABLE arbor_attribute ( + id INT PRIMARY KEY NOT NULL UNIQUE, + name VARCHAR(50) NOT NULL, + );''') + + cur.execute(''' + CREATE TABLE arbor_probe ( + id INT PRIMARY KEY NOT NULL UNIQUE, + cell_id INT NOT NULL, + segment_id INT NOT NULL, + position FLOAT, + attribute_id INT NOT NULL, + simulation_node_id INT, + FOREIGN KEY (simulation_node_id) REFERENCES arbor_simulation_node (id), + FOREIGN KEY (cell_id) REFERENCES arbor_cell (id), + FOREIGN KEY (attribute_id) REFERENCES arbor_attribute (id) + );''') + + + + con.commit() + con.close() + print("Arbor tables created successfully!\n") def main(): - # This is just to give the info-node some time to start the server - # in the docker container - time.sleep(5) - - # get info node - with open('access_node//info_node.json', 'r') as f: - info = json.load(f) - nodes.info_node = info['address'] - - # get simulation nodes - node_type = 'nest_simulation' - nodes.simulation_nodes = requests.get( - nodes.info_node+'/nodes', params={"node_type": node_type}).json() - - # run acces_node - 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'}) - CORS(app.app) - app.run(port=8080) + # Connect to the Database and initalize basic Table structure + SetupNestTables('postgres', 'docker', 5432) + #SetupArborTables('postgres', 'docker', 5432) + + # Wait for simulation nodes to post to database + time.sleep(5) + + + # get simulation nodes + con = ConnectToDatabase('postgres', 'docker', 5432) + cur = con.cursor() + # NEST + cur.execute("SELECT address FROM nest_simulation_node") + nodes.nest_simulation_nodes = [i[0] for i in cur.fetchall()] + # Arbor + #cur.execute("SELECT address FROM nest_simulation_node") + #nodes.nest_simulation_nodes = [i[0] for i in cur.fetchall()] + con.close() + + + # run acces_node + 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'}) + CORS(app.app) + app.run(port=8080) if __name__ == '__main__': diff --git a/access_node/controllers/arbor_controller.py b/access_node/controllers/arbor_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..4b8115a2c70cbc7f269f31510a1b33ee4e1086e1 --- /dev/null +++ b/access_node/controllers/arbor_controller.py @@ -0,0 +1,246 @@ +import connexion +import six + +from access_node.models.arbor_cell_properties import ArborCellProperties # noqa: E501 +from access_node.models.arbor_measurement import ArborMeasurement # noqa: E501 +from access_node.models.probe import Probe # noqa: E501 +from access_node.models.simulation_time_info import SimulationTimeInfo # noqa: E501 +from access_node.models.spikes import Spikes # noqa: E501 +from access_node import util + +from access_node.models.nodes import nodes +import requests +import psycopg2 +import numpy as np + + +def connect_to_database(): + return psycopg2.connect(database="postgres", user="postgres", + password="docker", host="database", port="5432") + + +def arbor_get_attributes(): # noqa: E501 + """Retrieves the list of all attributes. + + # noqa: E501 + + + :rtype: List[str] + """ + con = connect_to_database() + cur = con.cursor() + + cur.execute("SELECT name FROM arbor_attribute") + attributes = [i[0] for i in cur.fetchall()] + + con.close() + return attributes + + +def arbor_get_cell_ids(): # noqa: E501 + """Retrieves the list of all cell ids. + + # noqa: E501 + + + :rtype: List[int] + """ + con = connect_to_database() + cur = con.cursor() + + cur.execute("SELECT id FROM arbor_cell") + cell_ids = [i[0] for i in cur.fetchall()] + + con.close() + return cell_ids + + +def arbor_get_cell_properties(cell_ids=None): # noqa: E501 + """Retrieves the properties of the specified cells. + + # noqa: E501 + + :param cell_ids: A list of cell IDs queried for properties. + :type cell_ids: List[int] + + :rtype: List[ArborCellProperties] + """ + con = connect_to_database() + cur = con.cursor() + + if cell_ids == None: + cur.execute("SELECT cell_id, property FROM cell_property") + else: + cur.execute("SELECT cell_id, property FROM cell_property WHERE cell_property.cell_id IN %s", (tuple(cell_ids),)) + + properties = np.array(cur.fetchall()) + cell_ids = np.unique(properties[:,0]) + cell_properties = [] + for i in range(len(cell_ids)): + per_cell_properties = [] + for prop in properties: + if prop[0] == cell_ids[i]: + per_cell_properties.append(prop) + cell_properties.append(ArborCellProperties(cell_ids[i], per_cell_properties)) + + con.close() + return cell_properties + + +def arbor_get_measurements(attribute, probe_ids=None, _from=None, to=None, offset=None, limit=None): # noqa: E501 + """Retrieves the measurements for given probes (optional). + + # noqa: E501 + + :param attribute: The attribute to query (e.g., 'V_m' for the membrane potential) + :type attribute: str + :param probe_ids: A list of probes ids queried for data. + :type probe_ids: List[int] + :param _from: The start time (including) to be queried. + :type _from: float + :param to: The end time (excluding) to be queried. + :type to: float + :param offset: The offset into the result. + :type offset: int + :param limit: The maximum of entries to be result. + :type limit: int + + :rtype: ArborMeasurement + """ + if probe_ids == None: + probes = arbor_get_probes() + probe_ids = [] + for probe in probes: + probe_ids.append(probe.id) + + init = True + sim_times = [] + measurement = ArborMeasurement([], [], []) + for node in nodes.arbor_simulation_nodes: + response = requests.get( + 'http://'+node+'/arbor/measurements', params={"attribute": attribute, "probe_ids": probe_ids, "_from": _from, "to": to}).json() + if init: + sim_times = response['simulation_times'] + measurement = ArborMeasurement( + sim_times, probe_ids, [None for x in range(0, (len(sim_times)*len(probe_ids)))]) + init = False + for x in range(len(response['probe_ids'])): + m_id = response['probe_ids'][x] + index = measurement.probe_ids.index(m_id) + index_offset = index * len(sim_times) + for y in range(len(sim_times)): + measurement.values[index_offset + + y] = response['values'][x*len(sim_times)+y] + + # offset and limit + if (offset is None): + offset = 0 + if (limit is None or (limit + offset) > len(measurement.probe_ids)): + limit = len(measurement.probe_ids) - offset + measurement.probe_ids = measurement.probe_ids[offset:offset+limit] + measurement.values = measurement.values[offset * + len(sim_times):(offset+limit)*len(sim_times)] + + return measurement + + +def arbor_get_probes(attribute=None): # noqa: E501 + """Retrieves the list of all probes for a given attribute (optional). + + # noqa: E501 + + :param attribute: The attribute measured for which existing probes will be returned. + :type attribute: str + + :rtype: List[Probe] + """ + con = connect_to_database() + cur = con.cursor() + + if attribute == None: + cur.execute('''SELECT + arbor_probe.id, + arbor_probe.cell_id, + arbor_probe.segment_id, + arbor_probe.position, + FROM arbor_probe''') + else: + cur.execute('''SELECT + arbor_probe.id, + arbor_probe.cell_id, + arbor_probe.segment_id, + arbor_probe.position, + FROM arbor_probe INNER JOIN arbor_attribute + ON arbor_probe.attribute_id = arbor_attribute.id + WHERE arbor_attribute.name = %s''', (attribute,)) + + probes = np.array(cur.fetchall()) + con.close() + + return [Probe(*probe) for probe in probes] + + +def arbor_get_simulation_time_info(): # noqa: E501 + """Retrieves simulation time information. + + # noqa: E501 + + + :rtype: SimulationTimeInfo + """ + con = connect_to_database() + cur = con.cursor() + + cur.execute("SELECT MIN(current_simulation_time) FROM arbor_simulation_node") + current_time = cur.fetchall()[0][0] + + con.close() + + # TODO Add Start and End time when available + time_info = SimulationTimeInfo(current=current_time) + return time_info + + +def arbor_get_spikes(_from=None, to=None, cell_ids=None, segment_ids=None, offset=None, limit=None): # noqa: E501 + """Retrieves the spikes for the given simulation times (optional), cell and segment (optional). + + # noqa: E501 + + :param _from: The start time (including) to be queried. + :type _from: float + :param to: The end time (excluding) to be queried. + :type to: float + :param cell_ids: A list of cell ids queried for spike data. + :type cell_ids: List[int] + :param segment_ids: A list of segment ids queried for spike data. + :type segment_ids: List[int] + :param offset: The offset into the result. + :type offset: int + :param limit: The maximum of entries to be result. + :type limit: int + + :rtype: Spikes + """ + spikes = Spikes([], []) + for node in nodes.arbor_simulation_nodes: + response = requests.get( + 'http://'+node+'/arbor/spikes', params={"from": _from, "to": to, "cell_ids": cell_ids, "segment_ids": segment_ids}).json() + for x in range(len(response['simulation_times'])): + spikes.simulation_times.append(response['simulation_times'][x]) + spikes.gids.append(response['gids'][x]) + + # sort + sorted_ids = [x for _, x in sorted( + zip(spikes.simulation_times, spikes.gids))] + spikes.gids = sorted_ids + spikes.simulation_times.sort() + + # offset and limit + if (offset is None): + offset = 0 + if (limit is None or (limit + offset) > len(spikes.gids)): + limit = len(spikes.gids) - offset + spikes.gids = spikes.gids[offset:offset+limit] + spikes.simulation_times = spikes.simulation_times[offset:offset+limit] + + return spikes diff --git a/access_node/controllers/nest_controller.py b/access_node/controllers/nest_controller.py index 0e907f4c0aec80af97a79e7451bc4de9e6b705f2..c183e09d07f6a4e42f0b3015895ce44fec5ea9c6 100644 --- a/access_node/controllers/nest_controller.py +++ b/access_node/controllers/nest_controller.py @@ -3,16 +3,23 @@ import six from access_node.models.multimeter_info import MultimeterInfo # noqa: E501 from access_node.models.multimeter_measurement import MultimeterMeasurement # noqa: E501 -from access_node.models.neuron_properties import NeuronProperties # noqa: E501 +from access_node.models.nest_neuron_properties import NestNeuronProperties # noqa: E501 from access_node.models.simulation_time_info import SimulationTimeInfo # noqa: E501 from access_node.models.spikes import Spikes # noqa: E501 from access_node import util from access_node.models.nodes import nodes import requests +import psycopg2 +import numpy as np -def get_gids(): # noqa: E501 +def connect_to_database(): + return psycopg2.connect(database="postgres", user="postgres", + password="docker", host="database", port="5432") + + +def nest_get_gids(): # noqa: E501 """Retrieves the list of all GID. # noqa: E501 @@ -20,12 +27,18 @@ def get_gids(): # noqa: E501 :rtype: List[int] """ - gids = requests.get(nodes.info_node+'/gids').json() + con = connect_to_database() + cur = con.cursor() + + cur.execute("SELECT id FROM nest_neuron") + gids = [i[0] for i in cur.fetchall()] + + con.close() return gids -def get_gids_in_population(population_id): # noqa: E501 - """Retrieves the list of all neuron IDs. +def nest_get_gids_in_population(population_id): # noqa: E501 + """Retrieves the list of all neuron IDs within the population. # noqa: E501 @@ -34,25 +47,50 @@ def get_gids_in_population(population_id): # noqa: E501 :rtype: List[int] """ - gids = requests.get(nodes.info_node+'/population/$' + - str(population_id)+'/gids').json() + con = connect_to_database() + cur = con.cursor() + + cur.execute("SELECT id FROM nest_neuron WHERE nest_neuron.population_id ="+str(population_id)) + gids = [i[0] for i in cur.fetchall()] + + con.close() return gids -def get_multimeter_info(): # noqa: E501 +def nest_get_multimeter_info(): # noqa: E501 """Retreives the available multimeters and their properties. - + # noqa: E501 :rtype: MultimeterInfo """ - multimeter_info = requests.get(nodes.info_node+'/multimeter_info').json() - return multimeter_info + con = connect_to_database() + cur = con.cursor() + + cur.execute("SELECT * FROM nest_multimeter;") + attributes = np.array(cur.fetchall()) + + + gids = [] + if len(attributes) > 0: + for id in attributes[:,0]: + cur.execute("SELECT neuron_id FROM nest_neuron_multimeter WHERE multimeter_id = %s", (id,)) + gids.append([i[0] for i in cur.fetchall()]) + mult_info = [] + for i in range(len(attributes)): + mult_info.append({"id": attributes[i][0], + "attributes": attributes[i][1], + "gids": gids[i]}) -def get_multimeter_measurements(multimeter_id, attribute, _from=None, to=None, gids=None, offset=None, limit=None): # noqa: E501 - """Retrieves the measurements for a multimeter (optional) and GIDS (optional). + + con.close() + return mult_info + + +def nest_get_multimeter_measurements(multimeter_id, attribute, _from=None, to=None, gids=None, offset=None, limit=None): # noqa: E501 + """Retrieves the measurements for a multimeter, attribute and GIDS (optional). # noqa: E501 @@ -73,7 +111,7 @@ def get_multimeter_measurements(multimeter_id, attribute, _from=None, to=None, g :rtype: MultimeterMeasurement """ - mult_info = get_multimeter_info() + mult_info = nest_get_multimeter_info() mult_gids = [] multimeter_exists = False @@ -81,35 +119,37 @@ def get_multimeter_measurements(multimeter_id, attribute, _from=None, to=None, g if mult['id'] == multimeter_id: multimeter_exists = True if attribute not in mult['attributes']: - return Status(code=400, message="Given multimeter does not measure given attribute") + return "Given multimeter does not measure given attribute", 400 mult_gids = mult['gids'] break if not multimeter_exists: - return Status(code=400, message="Given multimeter does not exist") + return "Given multimeter does not exist", 400 if gids == None: gids = mult_gids else: for gid in gids: if gid not in mult_gids: - return Status(code=400, message="Gid "+str(gid)+" is not measured by given Multimeter") + return "Gid "+str(gid)+" is not measured by given Multimeter", 400 init = True sim_times = [] - measurement = MultimeterMeasurement([],[],[]) - for node in nodes.simulation_nodes: + measurement = MultimeterMeasurement([], [], []) + for node in nodes.nest_simulation_nodes: response = requests.get( - 'http://'+node+'/multimeter_measurement', params={"multimeter_id": multimeter_id, "attribute": attribute, "_from": _from, "to": to, "gids": gids}).json() + node+'/multimeter_measurement', params={"multimeter_id": multimeter_id, "attribute": attribute, "from": _from, "to": to, "gids": gids}).json() if init: sim_times = response['simulation_times'] - measurement = MultimeterMeasurement(sim_times, gids, [None for x in range(0,(len(sim_times)*len(gids)))]) + measurement = MultimeterMeasurement( + sim_times, gids, [None for x in range(0, (len(sim_times)*len(gids)))]) init = False for x in range(len(response['gids'])): gid = response['gids'][x] index = measurement.gids.index(gid) index_offset = index * len(sim_times) for y in range(len(sim_times)): - measurement.values[index_offset+y] = response['values'][x*len(sim_times)+y] + measurement.values[index_offset + + y] = response['values'][x*len(sim_times)+y] # offset and limit if (offset is None): @@ -117,12 +157,13 @@ def get_multimeter_measurements(multimeter_id, attribute, _from=None, to=None, g if (limit is None or (limit + offset) > len(measurement.gids)): limit = len(measurement.gids) - offset measurement.gids = measurement.gids[offset:offset+limit] - measurement.values = measurement.values[offset*len(sim_times):(offset+limit)*len(sim_times)] + measurement.values = measurement.values[offset * + len(sim_times):(offset+limit)*len(sim_times)] return measurement -def get_neuron_properties(gids=None): # noqa: E501 +def nest_get_neuron_properties(gids=None): # noqa: E501 """Retrieves the properties of the specified neurons. # noqa: E501 @@ -130,13 +171,37 @@ def get_neuron_properties(gids=None): # noqa: E501 :param gids: A list of GIDs queried for properties. :type gids: List[int] - :rtype: List[NeuronProperties] + :rtype: List[NestNeuronProperties] """ - properties = requests.get(nodes.info_node+'/neuron_properties').json() - return properties + con = connect_to_database() + cur = con.cursor() + cur.execute("Select * FROM nest_neuron LIMIT 0") + colnames = np.array([desc[0] for desc in cur.description]) + # column 1 and 2 contain the Node_id/Population_id and thus are removed + colnames = np.delete(colnames, [1,2]) -def get_populations(): # noqa: E501 + if gids == None: + cur.execute("Select * FROM nest_neuron") + else: + cur.execute("Select * FROM nest_neuron WHERE id IN %s", (tuple(gids),)) + + nest_properties = [] + properties = np.array(cur.fetchall()) + if properties.size != 0: + properties = np.delete(properties, [1,2], 1) + for k in range(len(properties[:,0])): + props = {} + id = properties[k,0] + for i in range(1, len(colnames)): + props.update({colnames[i]: properties[k,i] if properties[k,i] != None else []}) + nest_properties.append(NestNeuronProperties(id, props)) + + con.close() + return nest_properties + + +def nest_get_populations(): # noqa: E501 """Retrieves the list of all population IDs. # noqa: E501 @@ -144,11 +209,17 @@ def get_populations(): # noqa: E501 :rtype: List[int] """ - populations = requests.get(nodes.info_node+'/populations').json() + con = connect_to_database() + cur = con.cursor() + + cur.execute("SELECT DISTINCT (population_id) FROM nest_neuron") + populations = [i[0] for i in cur.fetchall()] + + con.close() return populations -def get_simulation_time_info(): # noqa: E501 +def nest_get_simulation_time_info(): # noqa: E501 """Retrieves simulation time information. # noqa: E501 @@ -156,11 +227,20 @@ def get_simulation_time_info(): # noqa: E501 :rtype: SimulationTimeInfo """ - time_info = requests.get(nodes.info_node+'/simulation_time_info').json() + con = connect_to_database() + cur = con.cursor() + + cur.execute("SELECT MIN(current_simulation_time) FROM nest_simulation_node") + current_time = cur.fetchall()[0][0] + + con.close() + + # TODO Add Start and End time when available + time_info = SimulationTimeInfo(current=current_time) return time_info -def get_spikes(_from=None, to=None, gids=None, offset=None, limit=None): # noqa: E501 +def nest_get_spikes(_from=None, to=None, gids=None, offset=None, limit=None): # noqa: E501 """Retrieves the spikes for the given simulation steps (optional) and GIDS (optional). # noqa: E501 @@ -179,9 +259,9 @@ def get_spikes(_from=None, to=None, gids=None, offset=None, limit=None): # noqa :rtype: Spikes """ spikes = Spikes([], []) - for node in nodes.simulation_nodes: + for node in nodes.nest_simulation_nodes: response = requests.get( - 'http://'+node+'/spikes', params={"_from": _from, "to": to, "gids": gids}).json() + node+'/spikes', params={"from": _from, "to": to, "gids": gids}).json() for x in range(len(response['simulation_times'])): spikes.simulation_times.append(response['simulation_times'][x]) spikes.gids.append(response['gids'][x]) @@ -203,7 +283,7 @@ def get_spikes(_from=None, to=None, gids=None, offset=None, limit=None): # noqa return spikes -def get_spikes_by_population(population_id, _from=None, to=None, offset=None, limit=None): # noqa: E501 +def nest_get_spikes_by_population(population_id, _from=None, to=None, offset=None, limit=None): # noqa: E501 """Retrieves the spikes for the given simulation steps (optional) and population. # noqa: E501 @@ -222,7 +302,7 @@ def get_spikes_by_population(population_id, _from=None, to=None, offset=None, li :rtype: Spikes """ spikes = Spikes([], []) - for node in nodes.simulation_nodes: + for node in nodes.nest_simulation_nodes: response = requests.get( node+'/population/'+population_id+'/spikes', params={"from": _from, "to": to}).json() for x in range(len(response['simulation_times'])): @@ -244,3 +324,4 @@ def get_spikes_by_population(population_id, _from=None, to=None, offset=None, li spikes.simulation_times = spikes.simulation_times[offset:offset+limit] return spikes + \ No newline at end of file diff --git a/access_node/models/__init__.py b/access_node/models/__init__.py index 477be9dfc6edecc6159fb56ff87b12f76a4ec25b..dfeebbec167737c6865bdcec6dd2fa7431f7d8d8 100644 --- a/access_node/models/__init__.py +++ b/access_node/models/__init__.py @@ -3,9 +3,12 @@ # flake8: noqa from __future__ import absolute_import # import models into model package +from access_node.models.arbor_cell_properties import ArborCellProperties +from access_node.models.arbor_measurement import ArborMeasurement from access_node.models.multimeter_info import MultimeterInfo from access_node.models.multimeter_info_inner import MultimeterInfoInner from access_node.models.multimeter_measurement import MultimeterMeasurement -from access_node.models.neuron_properties import NeuronProperties +from access_node.models.nest_neuron_properties import NestNeuronProperties +from access_node.models.probe import Probe from access_node.models.simulation_time_info import SimulationTimeInfo from access_node.models.spikes import Spikes diff --git a/access_node/models/arbor_cell_properties.py b/access_node/models/arbor_cell_properties.py new file mode 100644 index 0000000000000000000000000000000000000000..4a574fc6f38ac9e18b7cd07dc48a53eab5f4281a --- /dev/null +++ b/access_node/models/arbor_cell_properties.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 ArborCellProperties(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + def __init__(self, neuron_id: int=None, properties: object=None): # noqa: E501 + """ArborCellProperties - a model defined in Swagger + + :param neuron_id: The neuron_id of this ArborCellProperties. # noqa: E501 + :type neuron_id: int + :param properties: The properties of this ArborCellProperties. # noqa: E501 + :type properties: object + """ + self.swagger_types = { + 'neuron_id': int, + 'properties': object + } + + self.attribute_map = { + 'neuron_id': 'neuron_id', + 'properties': 'properties' + } + + self._neuron_id = neuron_id + self._properties = properties + + @classmethod + def from_dict(cls, dikt) -> 'ArborCellProperties': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The ArborCellProperties of this ArborCellProperties. # noqa: E501 + :rtype: ArborCellProperties + """ + return util.deserialize_model(dikt, cls) + + @property + def neuron_id(self) -> int: + """Gets the neuron_id of this ArborCellProperties. + + + :return: The neuron_id of this ArborCellProperties. + :rtype: int + """ + return self._neuron_id + + @neuron_id.setter + def neuron_id(self, neuron_id: int): + """Sets the neuron_id of this ArborCellProperties. + + + :param neuron_id: The neuron_id of this ArborCellProperties. + :type neuron_id: int + """ + + self._neuron_id = neuron_id + + @property + def properties(self) -> object: + """Gets the properties of this ArborCellProperties. + + + :return: The properties of this ArborCellProperties. + :rtype: object + """ + return self._properties + + @properties.setter + def properties(self, properties: object): + """Sets the properties of this ArborCellProperties. + + + :param properties: The properties of this ArborCellProperties. + :type properties: object + """ + + self._properties = properties diff --git a/access_node/models/arbor_measurement.py b/access_node/models/arbor_measurement.py new file mode 100644 index 0000000000000000000000000000000000000000..98146c824b1a436058204e4390f398072d16b481 --- /dev/null +++ b/access_node/models/arbor_measurement.py @@ -0,0 +1,120 @@ +# 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 ArborMeasurement(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + def __init__(self, simulation_times: List[float]=None, gids: List[int]=None, values: List[float]=None): # noqa: E501 + """ArborMeasurement - a model defined in Swagger + + :param simulation_times: The simulation_times of this ArborMeasurement. # noqa: E501 + :type simulation_times: List[float] + :param gids: The gids of this ArborMeasurement. # noqa: E501 + :type gids: List[int] + :param values: The values of this ArborMeasurement. # noqa: E501 + :type values: List[float] + """ + self.swagger_types = { + 'simulation_times': List[float], + 'gids': List[int], + 'values': List[float] + } + + self.attribute_map = { + 'simulation_times': 'simulation_times', + 'gids': 'gids', + 'values': 'values' + } + + self._simulation_times = simulation_times + self._gids = gids + self._values = values + + @classmethod + def from_dict(cls, dikt) -> 'ArborMeasurement': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The ArborMeasurement of this ArborMeasurement. # noqa: E501 + :rtype: ArborMeasurement + """ + return util.deserialize_model(dikt, cls) + + @property + def simulation_times(self) -> List[float]: + """Gets the simulation_times of this ArborMeasurement. + + This array is always sorted. # noqa: E501 + + :return: The simulation_times of this ArborMeasurement. + :rtype: List[float] + """ + return self._simulation_times + + @simulation_times.setter + def simulation_times(self, simulation_times: List[float]): + """Sets the simulation_times of this ArborMeasurement. + + This array is always sorted. # noqa: E501 + + :param simulation_times: The simulation_times of this ArborMeasurement. + :type simulation_times: List[float] + """ + + self._simulation_times = simulation_times + + @property + def gids(self) -> List[int]: + """Gets the gids of this ArborMeasurement. + + + :return: The gids of this ArborMeasurement. + :rtype: List[int] + """ + return self._gids + + @gids.setter + def gids(self, gids: List[int]): + """Sets the gids of this ArborMeasurement. + + + :param gids: The gids of this ArborMeasurement. + :type gids: List[int] + """ + + self._gids = gids + + @property + def values(self) -> List[float]: + """Gets the values of this ArborMeasurement. + + This array contains the measured values for each gid and time to get the value for gid n at time t you have to use the index n * length(simulation_times) + t # noqa: E501 + + :return: The values of this ArborMeasurement. + :rtype: List[float] + """ + return self._values + + @values.setter + def values(self, values: List[float]): + """Sets the values of this ArborMeasurement. + + This array contains the measured values for each gid and time to get the value for gid n at time t you have to use the index n * length(simulation_times) + t # noqa: E501 + + :param values: The values of this ArborMeasurement. + :type values: List[float] + """ + + self._values = values diff --git a/access_node/models/arbor_neuron_properties.py b/access_node/models/arbor_neuron_properties.py new file mode 100644 index 0000000000000000000000000000000000000000..f3d2d80e073ae5748198bd19fa63ca0d78b2e150 --- /dev/null +++ b/access_node/models/arbor_neuron_properties.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 ArborNeuronProperties(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + def __init__(self, neuron_id: int=None, properties: object=None): # noqa: E501 + """ArborNeuronProperties - a model defined in Swagger + + :param neuron_id: The neuron_id of this ArborNeuronProperties. # noqa: E501 + :type neuron_id: int + :param properties: The properties of this ArborNeuronProperties. # noqa: E501 + :type properties: object + """ + self.swagger_types = { + 'neuron_id': int, + 'properties': object + } + + self.attribute_map = { + 'neuron_id': 'neuron_id', + 'properties': 'properties' + } + + self._neuron_id = neuron_id + self._properties = properties + + @classmethod + def from_dict(cls, dikt) -> 'ArborNeuronProperties': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The ArborNeuronProperties of this ArborNeuronProperties. # noqa: E501 + :rtype: ArborNeuronProperties + """ + return util.deserialize_model(dikt, cls) + + @property + def neuron_id(self) -> int: + """Gets the neuron_id of this ArborNeuronProperties. + + + :return: The neuron_id of this ArborNeuronProperties. + :rtype: int + """ + return self._neuron_id + + @neuron_id.setter + def neuron_id(self, neuron_id: int): + """Sets the neuron_id of this ArborNeuronProperties. + + + :param neuron_id: The neuron_id of this ArborNeuronProperties. + :type neuron_id: int + """ + + self._neuron_id = neuron_id + + @property + def properties(self) -> object: + """Gets the properties of this ArborNeuronProperties. + + + :return: The properties of this ArborNeuronProperties. + :rtype: object + """ + return self._properties + + @properties.setter + def properties(self, properties: object): + """Sets the properties of this ArborNeuronProperties. + + + :param properties: The properties of this ArborNeuronProperties. + :type properties: object + """ + + self._properties = properties diff --git a/access_node/models/multimeter_measurement.py b/access_node/models/multimeter_measurement.py index 7c5b77bfaf18d042427abb033e03c2893209fb84..aa8bbb50b73f6710c728bf5f0f0f1f825bc83796 100644 --- a/access_node/models/multimeter_measurement.py +++ b/access_node/models/multimeter_measurement.py @@ -100,7 +100,7 @@ class MultimeterMeasurement(Model): def values(self) -> List[float]: """Gets the values of this MultimeterMeasurement. - This array contains the measured values for each gid and time to get the value for gid n at time t you have to use the index n * length(simulation_times) + t # noqa: E501 + This array contains the measured values for each probe and time to get the value for probe n at time t you have to use the index n * length(simulation_times) + t # noqa: E501 :return: The values of this MultimeterMeasurement. :rtype: List[float] @@ -111,7 +111,7 @@ class MultimeterMeasurement(Model): def values(self, values: List[float]): """Sets the values of this MultimeterMeasurement. - This array contains the measured values for each gid and time to get the value for gid n at time t you have to use the index n * length(simulation_times) + t # noqa: E501 + This array contains the measured values for each probe and time to get the value for probe n at time t you have to use the index n * length(simulation_times) + t # noqa: E501 :param values: The values of this MultimeterMeasurement. :type values: List[float] diff --git a/access_node/models/nest_neuron_properties.py b/access_node/models/nest_neuron_properties.py new file mode 100644 index 0000000000000000000000000000000000000000..335e6862ba1881d78ddb1cb30f0bd667ef0773fb --- /dev/null +++ b/access_node/models/nest_neuron_properties.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 NestNeuronProperties(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + def __init__(self, gid: int=None, properties: object=None): # noqa: E501 + """NestNeuronProperties - a model defined in Swagger + + :param gid: The gid of this NestNeuronProperties. # noqa: E501 + :type gid: int + :param properties: The properties of this NestNeuronProperties. # noqa: E501 + :type properties: object + """ + self.swagger_types = { + 'gid': int, + 'properties': object + } + + self.attribute_map = { + 'gid': 'gid', + 'properties': 'properties' + } + + self._gid = gid + self._properties = properties + + @classmethod + def from_dict(cls, dikt) -> 'NestNeuronProperties': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The NestNeuronProperties of this NestNeuronProperties. # noqa: E501 + :rtype: NestNeuronProperties + """ + return util.deserialize_model(dikt, cls) + + @property + def gid(self) -> int: + """Gets the gid of this NestNeuronProperties. + + + :return: The gid of this NestNeuronProperties. + :rtype: int + """ + return self._gid + + @gid.setter + def gid(self, gid: int): + """Sets the gid of this NestNeuronProperties. + + + :param gid: The gid of this NestNeuronProperties. + :type gid: int + """ + + self._gid = gid + + @property + def properties(self) -> object: + """Gets the properties of this NestNeuronProperties. + + + :return: The properties of this NestNeuronProperties. + :rtype: object + """ + return self._properties + + @properties.setter + def properties(self, properties: object): + """Sets the properties of this NestNeuronProperties. + + + :param properties: The properties of this NestNeuronProperties. + :type properties: object + """ + + self._properties = properties diff --git a/access_node/models/nodes.py b/access_node/models/nodes.py index 2da37ca1713b0bc2fe3f2e2cca854d9eee2130e9..f9c7d136e4838bdf71173810ece63065fd7744d6 100644 --- a/access_node/models/nodes.py +++ b/access_node/models/nodes.py @@ -1,8 +1,9 @@ from typing import List class Nodes(object): - def __init__(self, info_node: str=None, simulation_nodes: List[str]=None): + def __init__(self, info_node: str=None, nest_simulation_nodes: List[str]=None, arbor_simulation_nodes: List[str]=None): self.info_node = info_node - self.simulation_nodes = simulation_nodes + self.nest_simulation_nodes = nest_simulation_nodes + self.arbor_simulation_nodes = arbor_simulation_nodes nodes = Nodes() diff --git a/access_node/models/probe.py b/access_node/models/probe.py new file mode 100644 index 0000000000000000000000000000000000000000..d2b3c0caeb87117f5b68440d3f9b2014d9de7556 --- /dev/null +++ b/access_node/models/probe.py @@ -0,0 +1,142 @@ +# 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 Probe(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + def __init__(self, id: int=None, cell_id: int=None, segment_id: int=None, position: float=None): # noqa: E501 + """Probe - a model defined in Swagger + + :param id: The id of this Probe. # noqa: E501 + :type id: int + :param cell_id: The cell_id of this Probe. # noqa: E501 + :type cell_id: int + :param segment_id: The segment_id of this Probe. # noqa: E501 + :type segment_id: int + :param position: The position of this Probe. # noqa: E501 + :type position: float + """ + self.swagger_types = { + 'id': int, + 'cell_id': int, + 'segment_id': int, + 'position': float + } + + self.attribute_map = { + 'id': 'id', + 'cell_id': 'cell_id', + 'segment_id': 'segment_id', + 'position': 'position' + } + + self._id = id + self._cell_id = cell_id + self._segment_id = segment_id + self._position = position + + @classmethod + def from_dict(cls, dikt) -> 'Probe': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The Probe of this Probe. # noqa: E501 + :rtype: Probe + """ + return util.deserialize_model(dikt, cls) + + @property + def id(self) -> int: + """Gets the id of this Probe. + + + :return: The id of this Probe. + :rtype: int + """ + return self._id + + @id.setter + def id(self, id: int): + """Sets the id of this Probe. + + + :param id: The id of this Probe. + :type id: int + """ + + self._id = id + + @property + def cell_id(self) -> int: + """Gets the cell_id of this Probe. + + + :return: The cell_id of this Probe. + :rtype: int + """ + return self._cell_id + + @cell_id.setter + def cell_id(self, cell_id: int): + """Sets the cell_id of this Probe. + + + :param cell_id: The cell_id of this Probe. + :type cell_id: int + """ + + self._cell_id = cell_id + + @property + def segment_id(self) -> int: + """Gets the segment_id of this Probe. + + + :return: The segment_id of this Probe. + :rtype: int + """ + return self._segment_id + + @segment_id.setter + def segment_id(self, segment_id: int): + """Sets the segment_id of this Probe. + + + :param segment_id: The segment_id of this Probe. + :type segment_id: int + """ + + self._segment_id = segment_id + + @property + def position(self) -> float: + """Gets the position of this Probe. + + + :return: The position of this Probe. + :rtype: float + """ + return self._position + + @position.setter + def position(self, position: float): + """Sets the position of this Probe. + + + :param position: The position of this Probe. + :type position: float + """ + + self._position = position diff --git a/access_node/swagger/swagger.yaml b/access_node/swagger/swagger.yaml index e170db5f1a20d15fcff36f764ff41a12a3251c74..41798d174191a894534adef35403d71b644cffc1 100644 --- a/access_node/swagger/swagger.yaml +++ b/access_node/swagger/swagger.yaml @@ -10,12 +10,259 @@ schemes: - "https" - "http" paths: - /simulation_time_info: + /arbor/spikes: + get: + tags: + - "arbor" + summary: "Retrieves the spikes for the given simulation times (optional), cell\ + \ and segment (optional)." + operationId: "arbor_get_spikes" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "from" + in: "query" + description: "The start time (including) to be queried." + required: false + type: "number" + format: "double" + - name: "to" + in: "query" + description: "The end time (excluding) to be queried." + required: false + type: "number" + format: "double" + - name: "cell_ids" + in: "query" + description: "A list of cell ids queried for spike data." + required: false + type: "array" + items: + type: "integer" + format: "uint64" + - name: "segment_ids" + in: "query" + description: "A list of segment ids queried for spike data." + required: false + type: "array" + items: + type: "integer" + format: "uint64" + - name: "offset" + in: "query" + description: "The offset into the result." + required: false + type: "integer" + format: "uint64" + - name: "limit" + in: "query" + description: "The maximum of entries to be result." + required: false + type: "integer" + format: "uint64" + responses: + 200: + description: "Operation successful." + schema: + $ref: "#/definitions/Spikes" + 400: + description: "Operation failed." + schema: + type: "string" + example: "Error message" + x-swagger-router-controller: "access_node.controllers.arbor_controller" + /arbor/cell_ids: + get: + tags: + - "arbor" + summary: "Retrieves the list of all cell ids." + operationId: "arbor_get_cell_ids" + produces: + - "application/json" + parameters: [] + responses: + 200: + description: "Operation successful." + schema: + type: "array" + items: + type: "integer" + format: "uint64" + 400: + description: "Operation failed." + schema: + type: "string" + example: "Error message" + x-swagger-router-controller: "access_node.controllers.arbor_controller" + /arbor/probes: + get: + tags: + - "arbor" + summary: "Retrieves the list of all probes for a given attribute (optional)." + operationId: "arbor_get_probes" + produces: + - "application/json" + parameters: + - name: "attribute" + in: "query" + description: "The attribute measured for which existing probes will be returned." + required: false + type: "string" + responses: + 200: + description: "Operation successful." + schema: + type: "array" + items: + $ref: "#/definitions/Probe" + 400: + description: "Operation failed." + schema: + type: "string" + example: "Error message" + x-swagger-router-controller: "access_node.controllers.arbor_controller" + /arbor/attributes: + get: + tags: + - "arbor" + summary: "Retrieves the list of all attributes." + operationId: "arbor_get_attributes" + produces: + - "application/json" + parameters: [] + responses: + 200: + description: "Operation successful." + schema: + type: "array" + items: + type: "string" + example: + - "Voltage" + - "Current" + 400: + description: "Operation failed." + schema: + type: "string" + example: "Error message" + x-swagger-router-controller: "access_node.controllers.arbor_controller" + /arbor/measurements: + get: + tags: + - "arbor" + summary: "Retrieves the measurements for given probes (optional)." + operationId: "arbor_get_measurements" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "attribute" + in: "query" + description: "The attribute to query (e.g., 'V_m' for the membrane potential)" + required: true + type: "string" + - name: "probe_ids" + in: "query" + description: "A list of probes ids queried for data." + required: false + type: "array" + items: + type: "integer" + format: "uint64" + - name: "from" + in: "query" + description: "The start time (including) to be queried." + required: false + type: "number" + format: "double" + - name: "to" + in: "query" + description: "The end time (excluding) to be queried." + required: false + type: "number" + format: "double" + - name: "offset" + in: "query" + description: "The offset into the result." + required: false + type: "integer" + format: "uint64" + - name: "limit" + in: "query" + description: "The maximum of entries to be result." + required: false + type: "integer" + format: "uint64" + responses: + 200: + description: "Operation successful." + schema: + $ref: "#/definitions/ArborMeasurement" + 400: + description: "Operation failed." + schema: + type: "string" + example: "Error message" + x-swagger-router-controller: "access_node.controllers.arbor_controller" + /arbor/simulation_time_info: + get: + tags: + - "arbor" + summary: "Retrieves simulation time information." + operationId: "arbor_get_simulation_time_info" + produces: + - "application/json" + parameters: [] + responses: + 200: + description: "Operation successful." + schema: + $ref: "#/definitions/SimulationTimeInfo" + 400: + description: "Operation failed." + schema: + type: "string" + example: "Error message" + x-swagger-router-controller: "access_node.controllers.arbor_controller" + /arbor/cell_properties: + get: + tags: + - "arbor" + summary: "Retrieves the properties of the specified cells." + operationId: "arbor_get_cell_properties" + produces: + - "application/json" + parameters: + - name: "cell_ids" + in: "query" + description: "A list of cell IDs queried for properties." + required: false + type: "array" + items: + type: "integer" + format: "uint64" + responses: + 200: + description: "Operation successful." + schema: + type: "array" + items: + $ref: "#/definitions/ArborCellProperties" + 400: + description: "Operation failed." + schema: + type: "string" + example: "Error message" + x-swagger-router-controller: "access_node.controllers.arbor_controller" + /nest/simulation_time_info: get: tags: - "nest" summary: "Retrieves simulation time information." - operationId: "get_simulation_time_info" + operationId: "nest_get_simulation_time_info" produces: - "application/json" parameters: [] @@ -30,12 +277,12 @@ paths: type: "string" example: "Error message" x-swagger-router-controller: "access_node.controllers.nest_controller" - /gids: + /nest/gids: get: tags: - "nest" summary: "Retrieves the list of all GID." - operationId: "get_gids" + operationId: "nest_get_gids" produces: - "application/json" parameters: [] @@ -53,12 +300,12 @@ paths: type: "string" example: "Error message" x-swagger-router-controller: "access_node.controllers.nest_controller" - /neuron_properties: + /nest/neuron_properties: get: tags: - "nest" summary: "Retrieves the properties of the specified neurons." - operationId: "get_neuron_properties" + operationId: "nest_get_neuron_properties" produces: - "application/json" parameters: @@ -76,19 +323,19 @@ paths: schema: type: "array" items: - $ref: "#/definitions/NeuronProperties" + $ref: "#/definitions/NestNeuronProperties" 400: description: "Operation failed." schema: type: "string" example: "Error message" x-swagger-router-controller: "access_node.controllers.nest_controller" - /populations: + /nest/populations: get: tags: - "nest" summary: "Retrieves the list of all population IDs." - operationId: "get_populations" + operationId: "nest_get_populations" produces: - "application/json" parameters: [] @@ -106,13 +353,13 @@ paths: type: "string" example: "Error message" x-swagger-router-controller: "access_node.controllers.nest_controller" - /spikes: + /nest/spikes: get: tags: - "nest" summary: "Retrieves the spikes for the given simulation steps (optional) and\ \ GIDS (optional)." - operationId: "get_spikes" + operationId: "nest_get_spikes" consumes: - "application/json" produces: @@ -161,13 +408,13 @@ paths: type: "string" example: "Error message" x-swagger-router-controller: "access_node.controllers.nest_controller" - /population/${population_id}/spikes: + /nest/population/${population_id}/spikes: get: tags: - "nest" summary: "Retrieves the spikes for the given simulation steps (optional) and\ \ population." - operationId: "get_spikes_by_population" + operationId: "nest_get_spikes_by_population" consumes: - "application/json" produces: @@ -214,12 +461,12 @@ paths: type: "string" example: "Error message" x-swagger-router-controller: "access_node.controllers.nest_controller" - /multimeter_info: + /nest/multimeter_info: get: tags: - "nest" summary: "Retreives the available multimeters and their properties." - operationId: "get_multimeter_info" + operationId: "nest_get_multimeter_info" consumes: - "application/json" produces: @@ -236,12 +483,12 @@ paths: type: "string" example: "Error message" x-swagger-router-controller: "access_node.controllers.nest_controller" - /multimeter_measurement: + /nest/multimeter_measurement: get: tags: - "nest" - summary: "Retrieves the measurements for a multimeter (optional) and GIDS (optional)." - operationId: "get_multimeter_measurements" + summary: "Retrieves the measurements for a multimeter, attribute and GIDS (optional)." + operationId: "nest_get_multimeter_measurements" consumes: - "application/json" produces: @@ -301,12 +548,12 @@ paths: type: "string" example: "Error message" x-swagger-router-controller: "access_node.controllers.nest_controller" - /population/${population_id}/gids: + /nest/population/${population_id}/gids: get: tags: - "nest" - summary: "Retrieves the list of all neuron IDs." - operationId: "get_gids_in_population" + summary: "Retrieves the list of all neuron IDs within the population." + operationId: "nest_get_gids_in_population" produces: - "application/json" parameters: @@ -396,7 +643,78 @@ definitions: - 0.123 - 0.123 - 0.123 - NeuronProperties: + ArborMeasurement: + type: "object" + properties: + simulation_times: + type: "array" + description: "This array is always sorted." + items: + type: "number" + format: "double" + gids: + type: "array" + items: + type: "integer" + format: "uint64" + values: + type: "array" + description: "This array contains the measured values for each gid and time\ + \ to get the value for gid n at time t you have to use the index n * length(simulation_times)\ + \ + t" + items: + type: "number" + example: + simulation_times: + - 0.1 + - 0.2 + gids: + - 1 + - 2 + - 3 + values: + - 0.123 + - 0.123 + - 0.123 + - 0.123 + - 0.123 + - 0.123 + Probe: + type: "object" + properties: + id: + type: "integer" + format: "uint64" + cell_id: + type: "integer" + format: "uint64" + segment_id: + type: "integer" + format: "uint64" + position: + type: "number" + format: "double" + example: + id: 1 + cell_id: 2 + segment_id: 1 + position: 0.4 + ArborCellProperties: + type: "object" + properties: + neuron_id: + type: "integer" + format: "uint64" + properties: + type: "object" + properties: {} + example: + population: "pop1" + position: + - 0.1 + - 0.1 + - 0.1 + NestNeuronProperties: type: "object" properties: gid: diff --git a/access_node/test/test_arbor_controller.py b/access_node/test/test_arbor_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..951543a6098a102923e3c33c3741fe51c6f5163c --- /dev/null +++ b/access_node/test/test_arbor_controller.py @@ -0,0 +1,104 @@ +# coding: utf-8 + +from __future__ import absolute_import + +from flask import json +from six import BytesIO + +from access_node.models.arbor_measurement import ArborMeasurement # noqa: E501 +from access_node.models.arbor_neuron_properties import ArborNeuronProperties # noqa: E501 +from access_node.models.simulation_time_info import SimulationTimeInfo # noqa: E501 +from access_node.models.spikes import Spikes # noqa: E501 +from access_node.test import BaseTestCase + + +class TestArborController(BaseTestCase): + """ArborController integration test stubs""" + + def test_arbor_get_attributes(self): + """Test case for arbor_get_attributes + + Retrieves a list of measurable attributes + """ + response = self.client.open( + '//arbor/attributes', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_arbor_get_measurements(self): + """Test case for arbor_get_measurements + + Retrieves the measurements for given attribute and neuron_ids (optional). + """ + query_string = [('attribute', 'attribute_example'), + ('_from', 1.2), + ('to', 1.2), + ('neuron_ids', 56), + ('offset', 56), + ('limit', 56)] + response = self.client.open( + '//arbor/measurements', + method='GET', + content_type='application/json', + query_string=query_string) + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_arbor_get_neuron_ids(self): + """Test case for arbor_get_neuron_ids + + Retrieves the list of all neuron ids. + """ + response = self.client.open( + '//arbor/neuron_ids', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_arbor_get_neuron_properties(self): + """Test case for arbor_get_neuron_properties + + Retrieves the properties of the specified neurons. + """ + query_string = [('neuron_ids', 56)] + response = self.client.open( + '//arbor/neuron_properties', + method='GET', + query_string=query_string) + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_arbor_get_simulation_time_info(self): + """Test case for arbor_get_simulation_time_info + + Retrieves simulation time information. + """ + response = self.client.open( + '//arbor/simulation_time_info', + method='GET') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + def test_arbor_get_spikes(self): + """Test case for arbor_get_spikes + + Retrieves the spikes for the given simulation steps (optional) and GIDS (optional). + """ + query_string = [('_from', 1.2), + ('to', 1.2), + ('gids', 56), + ('offset', 56), + ('limit', 56)] + response = self.client.open( + '//arbor/spikes', + method='GET', + content_type='application/json', + query_string=query_string) + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/requirements.txt b/requirements.txt index b98be1375e9d2b8d9a3f951311897a6a878c0942..76172a29d0dc257df30e3c662f78d10acbee6092 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,7 @@ -connexion == 1.1.15 +connexion == 2.6.0 python_dateutil == 2.6.0 setuptools >= 21.0.0 requests flask_cors +psycopg2-binary +numpy \ No newline at end of file