Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions beacon_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
__author__ = CONFIG_INFO.author
__license__ = CONFIG_INFO.license
__copyright__ = CONFIG_INFO.copyright
__handover_drs__ = CONFIG_INFO.handover_drs
__handover_datasets__ = CONFIG_INFO.handover_datasets
__handover_beacon__ = CONFIG_INFO.handover_beacon
__handover_base__ = CONFIG_INFO.handover_base

__apiVersion__ = CONFIG_INFO.apiVersion
__beaconId__ = CONFIG_INFO.beaconId
Expand Down
10 changes: 7 additions & 3 deletions beacon_api/api/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
.. note:: See ``beacon_api`` root folder ``__init__.py`` for changing values used here.
"""

from .. import __apiVersion__, __title__, __version__, __description__, __url__, __alturl__
from .. import __apiVersion__, __title__, __version__, __description__, __url__, __alturl__, __handover_beacon__
from .. import __createtime__, __updatetime__, __org_id__, __org_name__, __org_description__
from .. import __org_address__, __org_logoUrl__, __org_welcomeUrl__, __org_info__, __org_contactUrl__
from ..utils.data_query import fetch_dataset_metadata
from .. import __handover_drs__
from ..utils.data_query import fetch_dataset_metadata, make_handover
from aiocache import cached
from aiocache.serializers import JsonSerializer

Expand All @@ -21,6 +22,7 @@ async def beacon_info(host, pool):
:return beacon_info: A dict that contain information about the ``Beacon`` endpoint.
"""
beacon_dataset = await fetch_dataset_metadata(pool)

# If one sets up a beacon it is recommended to adjust these sample requests
sample_allele_request = [{
"alternateBases": "G",
Expand Down Expand Up @@ -73,7 +75,9 @@ async def beacon_info(host, pool):
'updateDateTime': __updatetime__,
'datasets': beacon_dataset,
'sampleAlleleRequests': sample_allele_request,
'info': {"achievement": "World's first 1.0 Beacon"}
'info': {"achievement": "World's first 1.0 Beacon"},
}

if __handover_drs__:
beacon_info['beaconHandover'] = make_handover(__handover_beacon__, [x['id'] for x in beacon_dataset])
return beacon_info
7 changes: 5 additions & 2 deletions beacon_api/api/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"""

from ..utils.logging import LOG
from .. import __apiVersion__
from ..utils.data_query import filter_exists, find_datasets, fetch_datasets_access
from .. import __apiVersion__, __handover_beacon__, __handover_drs__
from ..utils.data_query import filter_exists, find_datasets, fetch_datasets_access, make_handover
from .exceptions import BeaconUnauthorised, BeaconForbidden, BeaconBadRequest


Expand Down Expand Up @@ -63,6 +63,7 @@ async def query_request_handler(params):
"""
LOG.info(f'{params[1]} request to beacon endpoint "/query"')
request = params[2]

# Fills the Beacon variable with the found data.
alleleRequest = {'referenceName': request.get("referenceName"),
'referenceBases': request.get("referenceBases"),
Expand Down Expand Up @@ -109,4 +110,6 @@ async def query_request_handler(params):
'alleleRequest': alleleRequest,
'datasetAlleleResponses': filter_exists(request.get("includeDatasetResponses", "NONE"), datasets)}

if __handover_drs__:
beacon_response['beaconHandover'] = make_handover(__handover_beacon__, [x['datasetId'] for x in datasets])
return beacon_response
9 changes: 9 additions & 0 deletions beacon_api/conf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
from collections import namedtuple


def parse_drspaths(paths):
"""Parse handover configuration."""
return [p.strip().split(',', 2) for p in paths.split('\n') if p.split()]


def parse_config_file(path):
"""Parse configuration file."""
config = ConfigParser()
Expand All @@ -15,6 +20,10 @@ def parse_config_file(path):
'author': config.get('beacon_general_info', 'author'),
'license': config.get('beacon_general_info', 'license'),
'copyright': config.get('beacon_general_info', 'copyright'),
'handover_drs': config.get('handover_info', 'drs', fallback=''),
'handover_datasets': parse_drspaths(config.get('handover_info', 'dataset_paths', fallback='')),
'handover_beacon': parse_drspaths(config.get('handover_info', 'beacon_paths', fallback='')),
'handover_base': int(config.get('handover_info', 'handover_base', fallback=0)),
'apiVersion': config.get('beacon_api_info', 'apiVersion'),
'beaconId': config.get('beacon_api_info', 'beaconId'),
'description': config.get('beacon_api_info', 'description'),
Expand Down
19 changes: 19 additions & 0 deletions beacon_api/conf/config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,25 @@ alturl=
createtime=2018-07-25T00:00:00Z


[handover_info]
# The base url for all handovers
# if this url is empty or commented, handover feature is disabled
#drs=https://examplebrowser.org

# Make the handovers 1- or 0-based
handover_base = 1

# Handovers for datasets
dataset_paths=
Variants,browse the variants matched by the query,dataset/{dataset}/browser/variant/{chr}-{start}-{ref}-{alt}
Region,browse data of the region matched by the query,dataset/{dataset}/browser/region/{chr}-{start}-{end}
Data,retrieve information of the datasets,dataset/{dataset}/browser

# Handovers for general beacon
beacon_paths=
Project,retrieve information about the datasets,dataset/{dataset}


[organisation_info]
# Globally unique identifier for organisation that hosts this Beacon service
org_id=fi.csc
Expand Down
29 changes: 29 additions & 0 deletions beacon_api/schemas/info.json
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,35 @@
},
"info": {
"type": "object"
},
"beaconHandover": {
"type": "array",
"required": [
"handoverType",
"url"
],
"properties": {
"handoverType": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
},
"label": {
"type": "string"
}
}
},
"description": {
"type": "string"
},
"url": {
"type": "string"
}
}
}
}
}
58 changes: 58 additions & 0 deletions beacon_api/schemas/response.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,35 @@
}
}
},
"beaconHandover": {
"type": "array",
"required": [
"handoverType",
"url"
],
"properties": {
"handoverType": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
},
"label": {
"type": "string"
}
}
},
"description": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"datasetAlleleResponses": {
"type": "array",
"items": {
Expand All @@ -129,6 +158,35 @@
}
]
},
"datasetHandover": {
"type": "array",
"required": [
"handoverType",
"url"
],
"properties": {
"handoverType": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
},
"label": {
"type": "string"
}
}
},
"description": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"referenceBases": {
"type": "string",
"pattern": "^([ACGT]+)$"
Expand Down
29 changes: 29 additions & 0 deletions beacon_api/utils/data_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .logging import LOG
from ..api.exceptions import BeaconServerError
from ..conf.config import DB_SCHEMA
from .. import __handover_drs__, __handover_datasets__, __handover_base__


# def transform_record(record, variantCount):
Expand Down Expand Up @@ -58,6 +59,31 @@ def transform_metadata(record):
return response


def add_handover(response):
"""Add handover to a dataset response."""
response["datasetHandover"] = make_handover(__handover_datasets__, [response['datasetId']],
response['referenceName'], response['start'],
response['end'], response['referenceBases'],
response['alternateBases'], response['variantType'])
return response


def make_handover(paths, datasetIds, chr='', start=0, end=0, ref='', alt='', variant=''):
"""Create one handover for each path (specified in config)."""
alt = alt if alt else variant
handovers = []
start = start + __handover_base__
end = end + __handover_base__
for label, desc, path in paths:
for dataset in set(datasetIds):
handovers.append({"handoverType": {"id": "CUSTOM", "label": label},
"description": desc,
"url": __handover_drs__ + path.format(dataset=dataset, chr=chr, start=start,
end=end, ref=ref, alt=alt)})

return handovers


async def fetch_datasets_access(db_pool, datasets):
"""Retrieve CONTROLLED datasets."""
public = []
Expand Down Expand Up @@ -184,6 +210,9 @@ async def fetch_filtered_dataset(db_pool, assembly_id, position, chromosome, ref
LOG.info(f"Query for dataset(s): {datasets} that are {access_type} matching conditions.")
for record in list(db_response):
processed = transform_misses(record) if misses else transform_record(record)
if __handover_drs__:
# If handover feature is enabled, add handover object to response
processed = add_handover(processed)
datasets.append(processed)
return datasets
except Exception as e:
Expand Down
18 changes: 18 additions & 0 deletions deploy/test/auth_test.ini
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ alturl=https://ega-archive.org/
createtime=2018-07-25T00:00:00Z


[handover_info]
# The base url for all handovers
drs=https://examplebrowser.org

# Make the handovers 1- or 0-based
handover_base = 1

# Handovers for datasets
dataset_paths=
Variants,browse the variants matched by the query,dataset/{dataset}/browser/variant/{chr}-{start}-{ref}-{alt}
Region,browse data of the region matched by the query,dataset/{dataset}/browser/region/{chr}-{start}-{end}
Data,retrieve information of the datasets,dataset/{dataset}/browser

# Handovers for general beacon
beacon_paths=
Project,retrieve information about the datasets,dataset/{dataset}


[organisation_info]
# Globally unique identifier for organisation that hosts this Beacon service
org_id=CSC
Expand Down
23 changes: 22 additions & 1 deletion tests/test_data_query.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asynctest
from unittest import mock
from beacon_api.utils.data_query import filter_exists, transform_record
from beacon_api.utils.data_query import transform_misses, transform_metadata, find_datasets
from beacon_api.utils.data_query import transform_misses, transform_metadata, find_datasets, add_handover, make_handover
from datetime import datetime
# from beacon_api.utils.data_query import fetch_dataset_metadata
from beacon_api.utils.data_query import handle_wildcard
Expand Down Expand Up @@ -110,6 +111,26 @@ def test_transform_metadata(self):
result = transform_metadata(record)
self.assertEqual(result, response)

def test_add_handover(self):
"""Test that add handover."""
# Test that the handover actually is added
handovers = [{"handover1": "info"}, {"handover2": "url"}]
record = {"datasetId": "test", "referenceName": "22", "referenceBases": "A",
"alternateBases": "C", "start": 10, "end": 11, "variantType": "SNP"}
with mock.patch('beacon_api.utils.data_query.make_handover', return_value=handovers):
result = add_handover(record)
record['datasetHandover'] = handovers
self.assertEqual(result, record)

def test_make_handover(self):
"""Test make handover."""
paths = [('lab1', 'desc1', 'path1'), ('lab2', 'desc2', 'path2')]
result = make_handover(paths, ['id1', 'id2', 'id1'])
# The number of handovers = number of paths * number of unique datasets
self.assertEqual(len(result), 4)
self.assertIn("path1", result[0]["url"])
self.assertEqual(result[0]["description"], 'desc1')

@asynctest.mock.patch('beacon_api.utils.data_query.fetch_filtered_dataset')
async def test_find_datasets(self, mock_filtered):
"""Test find datasets."""
Expand Down