diff --git a/.github/workflows/conda-package-build.yml b/.github/workflows/conda-package-build.yml index 7cf8b7f..f66b513 100644 --- a/.github/workflows/conda-package-build.yml +++ b/.github/workflows/conda-package-build.yml @@ -2,66 +2,17 @@ name: build_publish_anaconda on: push: - branches: [ master ] + branches: + - '**' + tags: + - 'v*' pull_request: - branches: [ master ] - -jobs: - build-and-publish: - name: ${{ matrix.os }}, Python 3.${{ matrix.python-minor-version }} for conda deployment - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - max-parallel: 3 - matrix: - os: [ ubuntu-latest ] - python-minor-version: [9] - isMaster: - - ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/dev') }} - exclude: - - isMaster: false - os: ubuntu-latest - python-minor-version: 7 - - isMaster: false - os: ubuntu-latest - python-minor-version: 8 - - isMaster: false - os: macos-latest - python-minor-version: 7 - - isMaster: false - os: macos-latest - python-minor-version: 8 - - isMaster: false - os: macos-latest - python-minor-version: 9 - - isMaster: false - os: windows-latest - python-minor-version: 7 - - isMaster: false - os: windows-latest - python-minor-version: 8 - - isMaster: false - os: windows-latest - python-minor-version: 9 + branches: + - '**' + - steps: - - name: Chekout - uses: actions/checkout@v3 - - name: Determine publish - uses: haya14busa/action-cond@v1 - id: publish - with: - cond: ${{ contains(github.ref, 'master') || startsWith(github.ref, 'refs/heads/v') }} - if_true: 'true' - if_false: 'false' - - name: Build and Publish - uses: openalea/action-build-publish-anaconda@v0.1.3 - with: - conda: conda - mamba: true - python: ${{ matrix.python-minor-version }} - numpy: '20.0' - channels: openalea3, conda-forge - token: ${{ secrets.ANACONDA_TOKEN }} - publish: ${{ steps.publish.outputs.value }} - label: main \ No newline at end of file +jobs: + build: + uses: openalea/github-action-conda-build/.github/workflows/conda-package-build.yml@main + secrets: + anaconda_token: ${{ secrets.ANACONDA_TOKEN }} diff --git a/setup.py b/setup.py index bdea44d..2837f43 100644 --- a/setup.py +++ b/setup.py @@ -16,16 +16,12 @@ """ """ # ============================================================================== -from setuptools import setup, find_packages, Extension, Command +from setuptools import setup, find_namespace_packages + # ============================================================================== pkg_root_dir = 'src' -packages = find_packages(pkg_root_dir) -top_pkgs = [pkg for pkg in packages if len(pkg.split('.')) <= 2] -package_dir = dict([('', pkg_root_dir)] + - [(pkg, pkg_root_dir + "/" + pkg.replace('.', '/')) - for pkg in top_pkgs]) - +packages = find_namespace_packages(where='src', include=['openalea.*']) name = "agroservices" _version = {} @@ -41,9 +37,9 @@ This package is intended to be close to the webservice. Therefore the requests will have the same API that each web service. ''' -author= 'Christian Fournier, Marc Labadie, Christophe Pradal' -url='https://github.com/H2020-IPM-openalea/agroservices' -license="GPL-v3" +author = 'Christian Fournier, Marc Labadie, Christophe Pradal' +url = 'https://github.com/H2020-IPM-openalea/agroservices' +license = "CeCILL-C" setup( name=name, @@ -59,9 +55,9 @@ # package installation packages=packages, - package_dir=package_dir, + package_dir={'': 'src'}, zip_safe=False, # See MANIFEST.in include_package_data=True, - ) +) diff --git a/src/agroservices/__init__.py b/src/agroservices/__init__.py deleted file mode 100644 index f8d7d0b..0000000 --- a/src/agroservices/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- python -*- -# -# Copyright INRIA - CIRAD - INRA -# -# Distributed under the Cecill-C License. -# See accompanying file LICENSE.txt or copy at -# http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html -# -# ============================================================================== - - -import pkg_resources -from .version import version -__version__ = version -try: - version = pkg_resources.require("agroservices")[0].version - __version__ = version -except: - version = __version__ - -#import colorlog -#logger = colorlog.getLogger("agroservices") - -#from .extern.easydev.config_tools import CustomConfig - -# Initialise the config directory if not already done -#configuration = CustomConfig("agroservices", verbose=False) -#bspath = configuration.user_config_dir - -#from . import settings -#from .settings import * - -#from . import services -#from .services import * - -from . import ipm -from .ipm import * - - -# sub packages inside agroservices. - -#import mapping -#from . import apps - - - - - diff --git a/src/agroservices/ipm/ipm.py b/src/agroservices/ipm/ipm.py index 8bbda55..7dfb3b1 100644 --- a/src/agroservices/ipm/ipm.py +++ b/src/agroservices/ipm/ipm.py @@ -11,27 +11,32 @@ ################## Interface Python IPM using Bioservice ######################################################## import json -from jsf import JSF -from typing import Union from pathlib import Path -from agroservices.services import REST -from agroservices.ipm.datadir import datadir +from typing import Union + import agroservices.ipm.fakers as fakers import agroservices.ipm.fixes as fixes +from agroservices.ipm.datadir import datadir +from agroservices.services import REST __all__ = ["IPM"] + def load_model(dssid, model): model = fixes.fix_prior_load_model(dssid, model) if 'input_schema' in model['execution']: - model['execution']['input_schema'] = json.loads(model['execution']['input_schema']) + model['execution']['input_schema'] = json.loads( + model['execution']['input_schema']) model = fixes.fix_load_model(dssid, model) return model + def read_dss(dss): - dss['models'] = {model["id"]: load_model(dss['id'], model) for model in dss["models"]} + dss['models'] = {model["id"]: load_model(dss['id'], model) for model in + dss["models"]} return dss + class IPM(REST): """ Interface to the IPM https://ipmdecisions.nibio.no/ @@ -79,7 +84,8 @@ class IPM(REST): >>> ipm.post_schema_dss_yaml_validate() """ - def __init__(self, name='IPM', url="https://platform.ipmdecisions.net", callback=None, *args, **kwargs): + def __init__(self, name='IPM', url="https://platform.ipmdecisions.net", + callback=None, *args, **kwargs): """Constructor Parameters @@ -90,7 +96,7 @@ def __init__(self, name='IPM', url="https://platform.ipmdecisions.net", callback Use cache, by default False """ # hack ipmdecisions.net is down - #url = 'https://ipmdecisions.nibio.no' + # url = 'https://ipmdecisions.nibio.no' super().__init__( name=name, url=url, @@ -154,7 +160,8 @@ def get_schema_weatherdata(self) -> dict: # schema weather data validate - def post_schema_weatherdata_validate(self, jsonfile: Union[str, Path] = 'weather_data.json') -> dict: + def post_schema_weatherdata_validate(self, jsonfile: Union[ + str, Path] = 'weather_data.json') -> dict: """Validates the posted weather data against the Json schema Parameters @@ -180,7 +187,8 @@ def post_schema_weatherdata_validate(self, jsonfile: Union[str, Path] = 'weather ###################### WeatherAdaptaterService ############################# - def get_weatheradapter(self, source: dict, params: dict = None, credentials: dict = None) -> dict: + def get_weatheradapter(self, source: dict, params: dict = None, + credentials: dict = None) -> dict: """Call weatheradapter service for a given weatherdata source Parameters @@ -208,7 +216,8 @@ def get_weatheradapter(self, source: dict, params: dict = None, credentials: dic if params is None: params = fakers.weather_adapter_params(source) - endpoint = source['endpoint'].format(WEATHER_API_URL=self._url + '/api/wx') + endpoint = source['endpoint'].format( + WEATHER_API_URL=self._url + '/api/wx') if not source['authentication_type'] == 'CREDENTIALS': res = self.http_get(endpoint, params=params, frmt='json') @@ -222,7 +231,8 @@ def get_weatheradapter(self, source: dict, params: dict = None, credentials: dic # weatherdatasource - def get_weatherdatasource(self, source_id=None, access_type=None, authentication_type=None) -> list: + def get_weatherdatasource(self, source_id=None, access_type=None, + authentication_type=None) -> list: """Access a dict of available wetherdata sources, of a source referenced by its id Parameters @@ -250,7 +260,8 @@ def get_weatherdatasource(self, source_id=None, access_type=None, authentication for r in res: if 'geoJSON' in r['spatial']: if r['spatial']['geoJSON'] is not None: - r['spatial']['geoJSON'] = json.loads(r['spatial']['geoJSON']) + r['spatial']['geoJSON'] = json.loads( + r['spatial']['geoJSON']) sources = {item['id']: item for item in res} sources = fixes.fix_get_weatherdatasource(sources) @@ -258,15 +269,18 @@ def get_weatherdatasource(self, source_id=None, access_type=None, authentication if source_id is None: res = sources if access_type is not None: - res = {k: v for k, v in res.items() if v['access_type'] == access_type} + res = {k: v for k, v in res.items() if + v['access_type'] == access_type} if authentication_type is not None: - res = {k: v for k, v in res.items() if v['authentication_type'] == authentication_type} + res = {k: v for k, v in res.items() if + v['authentication_type'] == authentication_type} return res elif source_id in sources: return sources[source_id] else: raise ValueError( - "datasource error: source_id is not referencing a valid datasource: %s" % (','.join(sources.keys()))) + "datasource error: source_id is not referencing a valid datasource: %s" % ( + ','.join(sources.keys()))) def post_weatherdatasource_location( self, @@ -402,7 +416,8 @@ def get_dss(self, execution_type=None) -> dict: if execution_type is not None: filtered = {} for id, dss in all_dss.items(): - models = {k:v for k,v in dss['models'].items() if v['execution']['type'] == execution_type} + models = {k: v for k, v in dss['models'].items() if + v['execution']['type'] == execution_type} if len(models) > 0: dss['models'] = models filtered[id] = dss diff --git a/src/agroservices/phis/phis.py b/src/agroservices/phis/phis.py index 5ead450..4c9fe8d 100644 --- a/src/agroservices/phis/phis.py +++ b/src/agroservices/phis/phis.py @@ -9,360 +9,413 @@ # ============================================================================== import urllib +from urllib.parse import quote import requests import six +from agroservices.services import REST + # ============================================================================== -_ws_address = 'http://147.100.179.156:8080/phenomeapi/resources' +__all__ = ["phis"] + + +class Phis(REST): + # TODO: Complete with the up to date requests + def __init__(self, name='Phis', + url="http://147.100.202.17/m3p/rest/", + callback=None, *args, **kwargs): + super().__init__( + name=name, + url=url, + *args, **kwargs) + + self.callback = callback # use in all methods) + + def post_json(self, web_service, json_txt, timeout=10., + overwriting=False, **kwargs): + """ Function calling a web service + + :param web_service: (str) name of web service requested + :param json_txt: (str) data formatted as json + :param timeout: (float) timeout for connexion in seconds + :param overwriting: (bool) allowing to overwrite data or not + + :return + (dict) response of the server (standard http) + (bool) whether data has been overwritten or not + """ + overwrote = False + headers = {"Content-type": "application/json"} + response = requests.request(method='POST', + url=self.url + web_service, + headers=headers, data=json_txt, + params=kwargs, timeout=timeout) + if response.status_code == 200 and overwriting: + response = requests.request(method='PUT', + url=self.url + "/" + web_service, + headers=headers, data=json_txt, + params=kwargs, timeout=timeout) + overwrote = True + return response, overwrote + + def get(self, web_service, timeout=10., **kwargs): + """ + + :param web_service: (str) name of web service requested + :param timeout: (float) timeout for connexion in seconds + :param kwargs: (str) arguments relative to web service (see http://147.100.202.17/m3p/api-docs/) + :return: + (dict) response of the server (standard http) + """ + response = requests.request(method='GET', + url=self.url + web_service, + params=kwargs, timeout=timeout) + return response -def post_json(address, web_service, json_txt, timeout=10., overwriting=False, **kwargs): - """ Function calling a web service + def get_all_data(self, web_service, timeout=10., **kwargs): + """ - :param address: (str) network address of the service web - :param web_service: (str) name of web service requested - :param json_txt: (str) data formatted as json - :param timeout: (float) timeout for connexion in seconds - :param overwriting: (bool) allowing to overwrite data or not + :param web_service: (str) name of web service requested + :param timeout: (float) timeout for connexion in seconds + :param kwargs: (str) arguments relative to web service (see http://147.100.202.17/m3p/api-docs/) + :return: + (list of dict) data relative to web service and parameters + """ + current_page = 0 + total_pages = 1 + values = list() - :return - (dict) response of the server (standard http) - (bool) whether data has been overwrote or not - """ - overwrote = False - headers = {"Content-type": "application/json"} - response = requests.request(method='POST', url=address + "/" + web_service, headers=headers, data=json_txt, - params=kwargs, timeout=timeout) - if response.status_code == 200 and overwriting: - response = requests.request(method='PUT', url=address + "/" + web_service, headers=headers, data=json_txt, - params=kwargs, timeout=timeout) - overwrote = True - return response, overwrote - - -def get(address, web_service, timeout=10., **kwargs): - """ - - :param address: (str) network address of the service web - :param web_service: (str) name of web service requested - :param timeout: (float) timeout for connexion in seconds - :param kwargs: (str) arguments relative to web service (see http://147.99.7.5:8080/phenomeapi/api-docs/#/) - :return: - (dict) response of the server (standard http) - """ - response = requests.request(method='GET', url=address + "/" + web_service, params=kwargs, timeout=timeout) - return response - - -def get_all_data(address, web_service, timeout=10., **kwargs): - """ - - :param address: (str) network address of the service web - :param web_service: (str) name of web service requested - :param timeout: (float) timeout for connexion in seconds - :param kwargs: (str) arguments relative to web service (see http://147.99.7.5:8080/phenomeapi/api-docs/#/) - :return: - (list of dict) data relative to web service and parameters - """ - current_page = 0 - total_pages = 1 - values = list() - - # TODO remove 'plants' specificity as soon as web service delay fixed - if not web_service == 'plants': - kwargs['pageSize'] = 50000 - else: - kwargs['pageSize'] = 10 - - while total_pages > current_page: - kwargs['page'] = current_page - response = requests.request(method='GET', url=address + "/" + web_service, params=kwargs, timeout=timeout) - if response.status_code == 200: - values.extend(response.json()["result"]["data"]) + # TODO remove 'plants' specificity as soon as web service delay fixed + if not web_service == 'plants': + kwargs['pageSize'] = 50000 + else: + kwargs['pageSize'] = 10 + + while total_pages > current_page: + kwargs['page'] = current_page + response = requests.request(method='GET', + url=self.url + web_service, + params=kwargs, timeout=timeout) + if response.status_code == 200: + values.extend(response.json()) + elif response.status_code == 500: + raise Exception("Server error") + else: + raise Exception( + response.json()["result"]["message"]) + + if response.json()["metadata"]["pagination"] is None: + total_pages = 0 + else: + total_pages = response.json()["metadata"]["pagination"][ + "totalPages"] + current_page += 1 + + return values + + def ws_token(self, username='pheonoarch@lepse.inra.fr', + password='phenoarch'): + """ Get token for PHIS web service + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param username: (str) + :param password: (str) + :return: + (str) token value + """ + response = self.get('security/authenticate', username=username, + password=password) + if response.status_code in [200, 201]: + return response.json()['result'] elif response.status_code == 500: raise Exception("Server error") else: - raise Exception(response.json()["metadata"]["status"][0]["message"]) - - if response.json()["metadata"]["pagination"] is None: - total_pages = 0 + raise Exception(response.json()["result"]["message"]) + + def ws_projects(self, session_id, project_name=''): + """ Get all projects information if project_name is empty, or only information about project_name specified + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param project_name: (str) specify a project name to get detailed information + :return: + (list of dict) projects list (one value only in list if project_name specified) + """ + return self.get_all_data('projects/' + project_name, + sessionId=session_id) + + def ws_germplasms(self, session_id, experiment_uri=None, species_uri=None, + project_name=None, germplasm_uri=None): + """ Get information about genotypes in experiments + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str or list of str) experiment URI + :param species_uri: (str) specie URI + :param project_name: (str) not available + :param germplasm_uri: (str) if specified then experiment_uri, species_uri and project_name parameters are useless + :return: + (list of dict) genotypic information of genotypes used in specific experiments, or for specific specie + """ + if experiment_uri is None and species_uri is None and germplasm_uri is None: + raise Exception( + "You must specify one of experiment_uri, species_uri or germplasms_uri") + if isinstance(germplasm_uri, six.string_types): + return self.get_all_data('germplasms/' + quote( + germplasm_uri), sessionId=session_id) + else: + if isinstance(experiment_uri, list): + experiment_uri = ','.join(experiment_uri) + return self.get_all_data('germplasms', + sessionId=session_id, + experimentURI=experiment_uri, + speciesURI=species_uri, + projectName=project_name) + + def ws_environment(self, session_id, experiment_uri=None, + variable_category=None, + variables=None, facility=None, + start_date=None, end_date=None, plant_uri=None): + """ Get environment sensors values from PHIS web service + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str) An experiment URI + :param variable_category: (str) Categories available in environment + must one of the list : ["setpoint", "meteorological", "micrometeo", "setting"] + :param variables: (str or list of str) variables types + :param facility: (str) Environment location + :param start_date: (str) Date of the first data (superior or equal) ( Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS or + YYYY-MM-DD HH:MM:SSZZ (ZZ = +01:00) ) + :param end_date: (str) Date of the last data (inferior or equal)( Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS or + YYYY-MM-DD HH:MM:SSZZ (ZZ = +01:00) ) + :param plant_uri: (str) plant URI to get only values of concerned sensors + :return: + (list of dict) environmental data in respect to parameters + """ + if isinstance(variables, list): + variables = ','.join(variables) + if isinstance(plant_uri, six.string_types): + return self.get_all_data('plants/' + quote( + plant_uri) + '/environment', timeout=20., + sessionId=session_id, + experimentURI=experiment_uri, + variableCategory=variable_category, + variables=variables, facility=facility, + startDate=start_date, endDate=end_date) + else: + return self.get_all_data('environment', timeout=20., + sessionId=session_id, + experimentURI=experiment_uri, + variableCategory=variable_category, + variables=variables, facility=facility, + startDate=start_date, endDate=end_date) + + def ws_variables(self, session_id, experiment_uri, category='environment', + provider='lepse'): + """ Get variables information according to category specified + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str) an experiment URI + :param category: (str) variable categories available, + must be one of ['environment', 'imagery', 'watering', 'weighing', 'phenotyping'] + :param provider: (str) provider of imagery processing (only used for 'imagery' category), + might be 'lemnatec' before 2015, and then 'elcom or 'lepse' + :return: + (list of dict) available variables for an experiment + """ + return self.get_all_data('variables/category/' + category, + sessionId=session_id, + experimentURI=experiment_uri, + imageryProvider=provider) + + def ws_experiments(self, session_id, project_name=None, season=None, + experiment_uri=None): + """ Get all experiments information from a project or/and a season, or only information about experiment_uri + specified + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param project_name: (str) specify a project name to get specifics experiments information + :param season: (int or str) find experiments by season (eg. 2012, 2013...) + :param experiment_uri: (str) specify an experiment URI to get detailed information + :return: + (list of dict) experiments information + """ + if project_name is None and season is None and experiment_uri is None: + raise Exception( + "You must specify one parameter of project_name, season or experiment_uri") + if isinstance(experiment_uri, six.string_types): + return self.get_all_data('core/experiments/' + quote( + experiment_uri) + '/details', + sessionId=session_id) + else: + return self.get_all_data('core/experiments', + sessionId=session_id, + projectName=project_name, season=season) + + def ws_label_views(self, session_id, experiment_uri, camera_angle=None, + view_type=None, provider=None): + """ Get existing label views for a specific experiment + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str) an experiment URI + :param camera_angle: (int) angle of the camera (between 0 and 360°, usually each 30°) + :param view_type: (str) usually one of ['top', 'side'] + :param provider: (str) usually lemnatec or elcom (useful ??) + :return: + (list of dict) label views + """ + return self.get_all_data('experiments/' + quote( + experiment_uri) + '/labelViews', timeout=30., + sessionId=session_id, cameraAngle=camera_angle, + viewType=view_type, provider=provider) + + def ws_observation_variables(self, session_id, experiment_uri): + """ Get existing observation variables for a specific experiment + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str) an experiment URI + :return: + (list of dict) observation variables + """ + return self.get_all_data('experiments/' + quote( + experiment_uri) + '/observationVariables', + sessionId=session_id) + + def ws_weighing(self, session_id, experiment_uri, date=None, + variables_name=None): + """ Get weighing data for a specific experiment + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str) an experiment URI + :param date: (str) Retrieve weighing data which been produced a particular day. Format :yyyy-MM-dd + :param variables_name: (str or list of str) name of one or several weighing variables + might be one of ['weightBefore', 'weightAfter', 'weight'] + :return: + (list of dict) weighing data for a specific experiment + """ + if isinstance(variables_name, list): + variables_name = ','.join(variables_name) + return self.get_all_data('weighing', sessionId=session_id, + experimentURI=experiment_uri, date=date, + variablesName=variables_name) + + def ws_plants(self, session_id, experiment_uri, plant_alias=None, + germplasms_uri=None, plant_uri=None): + """ Get plants information for an experiment + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str) an experiment URI + :param plant_alias: (str) alias of plant, usually the name used in experimentation + :param germplasms_uri: (str) URI of a germplasm + :param plant_uri: (str) specify a plant URI to get detailed information + :return: + (list of dict) plants information + """ + if isinstance(plant_uri, six.string_types): + return self.get_all_data('plants/' + quote(plant_uri), + sessionId=session_id, + experimentURI=experiment_uri) + else: + return self.get_all_data('plants', timeout=60., + sessionId=session_id, + experimentURI=experiment_uri, + plantAlias=plant_alias, + germplasmsURI=germplasms_uri) + + def ws_plant_moves(self, session_id, experiment_uri, plant_uri, + start_date=None, + end_date=None): + """ Get plant moves data during an experimentation + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str) an experiment URI + :param plant_uri: (str) plant URI to get moves data + :param start_date: (str) retrieve move(s) which begin after or equals to this date + ( Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS or YYYY-MM-DD HH:MM:SSZZ (ZZ = +01:00) ) + :param end_date: (str) retrieve move(s) which end before to this date + ( Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS or YYYY-MM-DD HH:MM:SSZZ (ZZ = +01:00) ) + :return: + (list of dict) plant moves data + """ + return self.get_all_data('plants/' + quote( + plant_uri) + '/moves', + timeout=20., + sessionId=session_id, + experimentURI=experiment_uri, + startDate=start_date, endDate=end_date) + + def ws_watering(self, session_id, experiment_uri, date=None, provider=None, + variables_name=None, plant_uri=None): + """ Get watering data for a specific experiment + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str) an experiment URI + :param date: (str) retrieve watering data which been produced a particular day. Format :yyyy-MM-dd + :param provider: (str) origin of the data (useful ??) + :param variables_name: (str or list of str) name of one or several watering variables + might be one of ['weightBefore', 'weightAfter', 'weightAmount'] + :param plant_uri: (str) plant URI to get only values specified plant + :return: + (list of dict) watering data for a specific experiment + """ + if isinstance(variables_name, list): + variables_name = ','.join(variables_name) + if isinstance(plant_uri, six.string_types): + return self.get_all_data('plants/' + quote( + plant_uri) + '/watering', timeout=30., + sessionId=session_id, + experimentURI=experiment_uri, date=date, + provider=provider, + variablesName=variables_name) + else: + return self.get_all_data('watering', sessionId=session_id, + experimentURI=experiment_uri, date=date, + provider=provider, + variablesName=variables_name) + + def ws_images_analysis(self, session_id, experiment_uri, date=None, + provider=None, + label_view=None, variables_name=None, + plant_uri=None): + """ Get images analysis data for a specific experiment + See http://147.100.202.17/m3p/api-docs/ for exact documentation + + :param session_id: (str) token got from ws_token() + :param experiment_uri: (str) an experiment URI + :param date: (str) retrieve phenotypes data from images which have been took at a specific day . Format :yyyy-MM-dd + :param provider: (str) origin of the data + :param label_view: (str) label view, something like side0, side30, ..., side330, top0 + :param variables_name: (str or list of str) name of one or several weighing variables + See ws_variables(session_id, experiment_uri, category='imagery') for exact list + :param plant_uri: (str) plant URI to get only values specified plant + :return: + (list of dict) images analysis data for a specific experiment + """ + if isinstance(variables_name, list): + variables_name = ','.join(variables_name) + if isinstance(plant_uri, six.string_types): + return self.get_all_data('plants/' + quote( + plant_uri) + '/phenotypes', timeout=30., + sessionId=session_id, + experimentURI=experiment_uri, date=date, + provider=provider, + labelView=label_view, + variablesName=variables_name) else: - total_pages = response.json()["metadata"]["pagination"]["totalPages"] - current_page += 1 - - return values - - -def ws_token(username='guestphis@supagro.inra.fr', password='guestphis', address=_ws_address): - """ Get token for PHIS web service - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param username: (str) - :param password: (str) - :param address: (str) address of the web service, use global value by default - :return: - (str) token value - """ - response = get(address, 'token', username=username, password=password) - if response.status_code in [200, 201]: - return response.json()['session_token'] - elif response.status_code == 500: - raise Exception("Server error") - else: - raise Exception(response.json()["metadata"]["status"][0]["message"]) - - -def ws_projects(session_id, project_name='', address=_ws_address): - """ Get all projects information if project_name is empty, or only information about project_name specified - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param project_name: (str) specify a project name to get detailed information - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) projects list (one value only in list if project_name specified) - """ - return get_all_data(address, 'projects/' + project_name, sessionId=session_id) - - -def ws_germplasms(session_id, experiment_uri=None, species_uri=None, project_name=None, germplasm_uri=None, - address=_ws_address): - """ Get information about genotypes in experiments - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str or list of str) experiment URI - :param species_uri: (str) specie URI - :param project_name: (str) not available - :param germplasm_uri: (str) if specified then experiment_uri, species_uri and project_name parameters are useless - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) genotypic information of genotypes used in specific experiments, or for specific specie - """ - if experiment_uri is None and species_uri is None and germplasm_uri is None: - raise Exception("You must specify one of experiment_uri, species_uri or germplasms_uri") - if isinstance(germplasm_uri, six.string_types): - return get_all_data(address, 'germplasms/' + urllib.quote_plus(germplasm_uri), sessionId=session_id) - else: - if isinstance(experiment_uri, list): - experiment_uri = ','.join(experiment_uri) - return get_all_data(address, 'germplasms', sessionId=session_id, experimentURI=experiment_uri, - speciesURI=species_uri, projectName=project_name) - - -def ws_environment(session_id, experiment_uri=None, variable_category=None, variables=None, facility=None, - start_date=None, end_date=None, plant_uri=None, address=_ws_address): - """ Get environment sensors values from PHIS web service - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str) An experiment URI - :param variable_category: (str) Categories available in environment - must one of the list : ["setpoint", "meteorological", "micrometeo", "setting"] - :param variables: (str or list of str) variables types - :param facility: (str) Environment location - :param start_date: (str) Date of the first data (superior or equal) ( Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS or - YYYY-MM-DD HH:MM:SSZZ (ZZ = +01:00) ) - :param end_date: (str) Date of the last data (inferior or equal)( Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS or - YYYY-MM-DD HH:MM:SSZZ (ZZ = +01:00) ) - :param plant_uri: (str) plant URI to get only values of concerned sensors - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) environmental data in respect to parameters - """ - if isinstance(variables, list): - variables = ','.join(variables) - if isinstance(plant_uri, six.string_types): - return get_all_data(address, 'plants/' + urllib.quote_plus(plant_uri) + '/environment', timeout=20., - sessionId=session_id, experimentURI=experiment_uri, variableCategory=variable_category, - variables=variables, facility=facility, startDate=start_date, endDate=end_date) - else: - return get_all_data(address, 'environment', timeout=20., sessionId=session_id, experimentURI=experiment_uri, - variableCategory=variable_category, variables=variables, facility=facility, - startDate=start_date, endDate=end_date) - - -def ws_variables(session_id, experiment_uri, category='environment', provider='lepse', address=_ws_address): - """ Get variables information according to category specified - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str) an experiment URI - :param category: (str) variable categories available, - must be one of ['environment', 'imagery', 'watering', 'weighing', 'phenotyping'] - :param provider: (str) provider of imagery processing (only used for 'imagery' category), - might be 'lemnatec' before 2015, and then 'elcom or 'lepse' - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) available variables for an experiment - """ - return get_all_data(address, 'variables/category/' + category, sessionId=session_id, experimentURI=experiment_uri, - imageryProvider=provider) - - -def ws_experiments(session_id, project_name=None, season=None, experiment_uri=None, address=_ws_address): - """ Get all experiments information from a project or/and a season, or only information about experiment_uri - specified - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param project_name: (str) specify a project name to get specifics experiments information - :param season: (int or str) find experiments by season (eg. 2012, 2013...) - :param experiment_uri: (str) specify an experiment URI to get detailed information - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) experiments information - """ - if project_name is None and season is None and experiment_uri is None: - raise Exception("You must specify one parameter of project_name, season or experiment_uri") - if isinstance(experiment_uri, six.string_types): - return get_all_data(address, 'experiments/' + urllib.quote_plus(experiment_uri) + '/details', - sessionId=session_id) - else: - return get_all_data(address, 'experiments', sessionId=session_id, projectName=project_name, season=season) - - -def ws_label_views(session_id, experiment_uri, camera_angle=None, view_type=None, provider=None, address=_ws_address): - """ Get existing label views for a specific experiment - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str) an experiment URI - :param camera_angle: (int) angle of the camera (between 0 and 360°, usually each 30°) - :param view_type: (str) usually one of ['top', 'side'] - :param provider: (str) usually lemnatec or elcom (useful ??) - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) label views - """ - return get_all_data(address, 'experiments/' + urllib.quote_plus(experiment_uri) + '/labelViews', timeout=30., - sessionId=session_id, cameraAngle=camera_angle, viewType=view_type, provider=provider) - - -def ws_observation_variables(session_id, experiment_uri, address=_ws_address): - """ Get existing observation variables for a specific experiment - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str) an experiment URI - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) observation variables - """ - return get_all_data(address, 'experiments/' + urllib.quote_plus(experiment_uri) + '/observationVariables', - sessionId=session_id) - - -def ws_weighing(session_id, experiment_uri, date=None, variables_name=None, address=_ws_address): - """ Get weighing data for a specific experiment - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str) an experiment URI - :param date: (str) Retrieve weighing data which been produced a particular day. Format :yyyy-MM-dd - :param variables_name: (str or list of str) name of one or several weighing variables - might be one of ['weightBefore', 'weightAfter', 'weight'] - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) weighing data for a specific experiment - """ - if isinstance(variables_name, list): - variables_name = ','.join(variables_name) - return get_all_data(address, 'weighing', sessionId=session_id, experimentURI=experiment_uri, date=date, - variablesName=variables_name) - - -def ws_plants(session_id, experiment_uri, plant_alias=None, germplasms_uri=None, plant_uri=None, address=_ws_address): - """ Get plants information for an experiment - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str) an experiment URI - :param plant_alias: (str) alias of plant, usually the name used in experimentation - :param germplasms_uri: (str) URI of a germplasm - :param plant_uri: (str) specify a plant URI to get detailed information - :param address:(str) address of the web service, use global value by default - :return: - (list of dict) plants information - """ - if isinstance(plant_uri, six.string_types): - return get_all_data(address, 'plants/' + urllib.quote_plus(plant_uri), sessionId=session_id, - experimentURI=experiment_uri) - else: - return get_all_data(address, 'plants', timeout=60., sessionId=session_id, experimentURI=experiment_uri, - plantAlias=plant_alias, germplasmsURI=germplasms_uri) - - -def ws_plant_moves(session_id, experiment_uri, plant_uri, start_date=None, end_date=None, address=_ws_address): - """ Get plant moves data during an experimentation - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str) an experiment URI - :param plant_uri: (str) plant URI to get moves data - :param start_date: (str) retrieve move(s) which begin after or equals to this date - ( Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS or YYYY-MM-DD HH:MM:SSZZ (ZZ = +01:00) ) - :param end_date: (str) retrieve move(s) which end before to this date - ( Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS or YYYY-MM-DD HH:MM:SSZZ (ZZ = +01:00) ) - :param address:(str) address of the web service, use global value by default - :return: - (list of dict) plant moves data - """ - return get_all_data(address, 'plants/' + urllib.quote_plus(plant_uri) + '/moves', timeout=20., - sessionId=session_id, experimentURI=experiment_uri, startDate=start_date, endDate=end_date) - - -def ws_watering(session_id, experiment_uri, date=None, provider=None, variables_name=None, plant_uri=None, - address=_ws_address): - """ Get watering data for a specific experiment - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str) an experiment URI - :param date: (str) retrieve watering data which been produced a particular day. Format :yyyy-MM-dd - :param provider: (str) origin of the data (useful ??) - :param variables_name: (str or list of str) name of one or several watering variables - might be one of ['weightBefore', 'weightAfter', 'weightAmount'] - :param plant_uri: (str) plant URI to get only values specified plant - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) watering data for a specific experiment - """ - if isinstance(variables_name, list): - variables_name = ','.join(variables_name) - if isinstance(plant_uri, six.string_types): - return get_all_data(address, 'plants/' + urllib.quote_plus(plant_uri) + '/watering', timeout=30., - sessionId=session_id, experimentURI=experiment_uri, date=date, provider=provider, - variablesName=variables_name) - else: - return get_all_data(address, 'watering', sessionId=session_id, experimentURI=experiment_uri, date=date, - provider=provider, variablesName=variables_name) - - -def ws_images_analysis(session_id, experiment_uri, date=None, provider=None, label_view=None, variables_name=None, - plant_uri=None, address=_ws_address): - """ Get images analysis data for a specific experiment - See http://147.99.7.5:8080/phenomeapi/api-docs/#/ for exact documentation - - :param session_id: (str) token got from ws_token() - :param experiment_uri: (str) an experiment URI - :param date: (str) retrieve phenotypes data from images which have been took at a specific day . Format :yyyy-MM-dd - :param provider: (str) origin of the data - :param label_view: (str) label view, something like side0, side30, ..., side330, top0 - :param variables_name: (str or list of str) name of one or several weighing variables - See ws_variables(session_id, experiment_uri, category='imagery') for exact list - :param plant_uri: (str) plant URI to get only values specified plant - :param address: (str) address of the web service, use global value by default - :return: - (list of dict) images analysis data for a specific experiment - """ - if isinstance(variables_name, list): - variables_name = ','.join(variables_name) - if isinstance(plant_uri, six.string_types): - return get_all_data(address, 'plants/' + urllib.quote_plus(plant_uri) + '/phenotypes', timeout=30., - sessionId=session_id, experimentURI=experiment_uri, date=date, provider=provider, - labelView=label_view, variablesName=variables_name) - else: - return get_all_data(address, 'imagesAnalysis', timeout=30., sessionId=session_id, experimentURI=experiment_uri, - date=date, provider=provider, labelView=label_view, variablesName=variables_name) + return self.get_all_data('imagesAnalysis', timeout=30., + sessionId=session_id, + experimentURI=experiment_uri, + date=date, provider=provider, + labelView=label_view, + variablesName=variables_name) diff --git a/test/test_phis.py b/test/test_phis.py new file mode 100644 index 0000000..38e314b --- /dev/null +++ b/test/test_phis.py @@ -0,0 +1,39 @@ +import requests +from agroservices.phis.phis import Phis + + +def test_url(): + phis = Phis() + assert phis.url is not None + try: + requests.get(phis.url) + except Exception as err: + assert False, err + else: + assert True + + +def test_token(): + phis = Phis() + json = '{ \ + "identifier": "phenoarch@lepse.inra.fr",\ + "password": "phenoarch"\ + }' + + response, _ = phis.post_json('security/authenticate', json) + token = response.json()['result']['token'] + assert len(token) > 1 + + +def test_ws_experiments(): + phis = Phis() + json = '{ \ + "identifier": "phenoarch@lepse.inra.fr",\ + "password": "phenoarch"\ + }' + + response, _ = phis.post_json('security/authenticate', json) + token = response.json()['result']['token'] + data = phis.ws_experiments(experiment_uri='m3p:id/experiment/g2was2022', + session_id=token) + print(data) \ No newline at end of file