From fc75de0dfe8a62289d9cfb1f4255f953f662ce60 Mon Sep 17 00:00:00 2001 From: Khoroshevskyi Date: Fri, 1 Mar 2024 16:34:14 +0100 Subject: [PATCH] Fixed #72 --- README.md | 20 ++- bedhost/_version.py | 2 +- bedhost/const.py | 35 ----- bedhost/helpers.py | 218 +++++++++++++++--------------- requirements/requirements-all.txt | 5 +- 5 files changed, 128 insertions(+), 152 deletions(-) diff --git a/README.md b/README.md index 8fb913b4..422e443c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,25 @@ -# bedhost +

bedhost

+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Github badge](https://img.shields.io/badge/source-github-354a75?logo=github)](https://github.com/databio/bedhost) + `bedhost` is a Python FastAPI module for the API that powers BEDbase It needs a path to the *bedbase configuration file*, which can be provided either via `-c`/`--config` argument or read from `$BEDBASE_CONFIG` environment variable. +--- + +**Deployed public instance**: https://bedbase.org/ + +**Documentation**: https://docs.bedbase.org/bedhost + +**API**: https://api.bedbase.org/ + +**Source Code**: https://github.com/databio/bedhost/ + +--- + + ## Running for development Running with `uvicorn` provides auto-reload. To configure, this assumes you have previously set up `databio/secrets`. @@ -29,6 +45,7 @@ Now, you can access the service at [http://127.0.0.1:8000](http://127.0.0.1:8000 - 127.0.0.1:8000/bed/78c0e4753d04b238fc07e4ebe5a02984/metadata - 127.0.0.1:8000/bed/78c0e4753d04b238fc07e4ebe5a02984/metadata?attr_ids=md5sum&attr_ids=genome +---- ## Running the server in Docker ### Building image @@ -43,6 +60,7 @@ Existing images can be found [at dockerhub](https://hub.docker.com/r/databio/bed Configuration settings and deployment instructions are in the `bedbase.org` repository. +--- ## Deploying updates automatically diff --git a/bedhost/_version.py b/bedhost/_version.py index d3ec452c..493f7415 100644 --- a/bedhost/_version.py +++ b/bedhost/_version.py @@ -1 +1 @@ -__version__ = "0.2.0" +__version__ = "0.3.0" diff --git a/bedhost/const.py b/bedhost/const.py index 96332ef8..c4c96e82 100644 --- a/bedhost/const.py +++ b/bedhost/const.py @@ -32,41 +32,6 @@ "number": {"min": 0, "step": 0.01}, "string": None, } -NUMERIC_OPERATORS = [ - "equal", - "not_equal", - "greater", - "greater_or_equal", - "between", - "less", - "less_or_equal", - "is_null", - "is_not_null", -] -TEXT_OPERATORS = ["equal", "not_equal", "in", "not_in", "is_null", "is_not_null"] -OPERATORS_MAPPING = { - "string": TEXT_OPERATORS, - "number": NUMERIC_OPERATORS, - "integer": NUMERIC_OPERATORS, -} -INIT_POSTGRES_CONDITION = "gc_content>0.5" -INIT_QUERYBUILDER = { - "condition": "AND", - "rules": [ - { - "id": "gc_content", - "field": "gc_content", - "type": "double", - "input": "number", - "operator": "greater", - "value": 0.5, - } - ], - "valid": True, -} -CUR_RESULT = "current_result" -CUR_RULES = "current_rules" - class FIG_FORMAT(str, Enum): png = "png" diff --git a/bedhost/helpers.py b/bedhost/helpers.py index f20acf82..5f6b7f5e 100644 --- a/bedhost/helpers.py +++ b/bedhost/helpers.py @@ -2,15 +2,8 @@ from bbconf import BedBaseConf from starlette.responses import FileResponse, RedirectResponse, JSONResponse -from typing import List -from urllib import parse from . import _LOGGER -from .const import ( - TYPES_MAPPING, - VALIDATIONS_MAPPING, - OPERATORS_MAPPING, -) from .exceptions import BedHostException @@ -22,6 +15,7 @@ class BedHostConf(BedBaseConf): def __init__(self, config_path: str = None): super().__init__(config_path) + # TODO: should it be moved to bbconf? def serve_file(self, path: str, remote: bool = None): """ Serve a local or remote file @@ -73,111 +67,111 @@ def bedset_retrieve(self, digest: str, column: str) -> dict: except KeyError: return {} - -def get_search_setup(schema: dict): - """ - Create a query setup for QueryBuilder to interface the DB. - - :param dict schema: pipestat schema - :return list[dict]: a list dictionaries with search setup to use - in QueryBuilder - """ - setup_dicts = [] - for col_name, col_schema in schema.items(): - try: - setup_dict = { - "id": col_name, - "label": col_schema["description"], - "type": TYPES_MAPPING[col_schema["type"]], - "validation": VALIDATIONS_MAPPING[col_schema["type"]], - "operators": OPERATORS_MAPPING[col_schema["type"]], - } - except (AttributeError, KeyError): - _LOGGER.warning( - f"Database column '{col_name}' of type " - f"'{col_schema['type']}' has no query builder " - f"settings predefined, skipping." - ) - else: - setup_dicts.append(setup_dict) - return setup_dicts - - -def construct_search_data(hits: list, request) -> List[List[str]]: - """ - Construct a list of links to display as the search result - - :param Iterable[str] hits: ids to compose the list for - :param starlette.requests.Request request: request for the context - :return Iterable[list]: results to display - """ - template_data = [] - for h in hits: - bed_data_url_template = ( - request.url_for("bedfile") + f"?md5sum={h['md5sum']}&format=" - ) - template_data.append( - [h["name"]] - + [bed_data_url_template + ext for ext in ["html", "bed", "json"]] - ) - return template_data - - -def get_mounted_symlink_path(symlink: str) -> str: - """ - Get path to the symlinks target on a mounted filesystem volume. - Accounts for both transformed and non-transformed symlink targets - - :param str symlink: absolute symlink path - :return str: path to the symlink target on the mounted volume - """ - - def _find_mount_point(path): - path = os.path.abspath(path) - while not os.path.ismount(path): - path = os.path.dirname(path) - return path - - link_tgt = os.readlink(symlink) - if not os.path.isabs(link_tgt): - _LOGGER.debug("Volume mounted with symlinks transformation") - return os.path.abspath(os.path.join(os.path.dirname(symlink), link_tgt)) - mnt_point = _find_mount_point(symlink) - first = os.path.relpath(symlink, mnt_point).split("/")[0] - common_idx = link_tgt.split("/").index(first) - rel_tgt = os.path.join(*link_tgt.split("/")[common_idx:]) - return os.path.join(mnt_point, rel_tgt) - - -def get_all_bedset_urls_mapping(bbc: BedBaseConf, request): - """ - Get a mapping of all bedset ids and corrsponding splaspages urls - - :param bbconf.BedBaseConf bbc: bedbase configuration object - :param starlette.requests.Request request: request context for url generation - :return Mapping: a mapping of bedset ids and the urls to the corresponding splashpages - """ - hits = bbc.bedset.backend.select(columns=["name", "md5sum"]) - if not hits: - return - # TODO: don't hardcode url path element name, use operationID? - return { - hit.name: get_param_url(request.url_for("bedsetsplash"), {"md5sum": hit.md5sum}) - for hit in hits - } - - -def get_param_url(url, params): - """ - Create parametrized URL - - :param str url: URL base to parametrize - :param Mapping params: a mapping of URL parameters and values - :return str: parametrized URL - """ - if not params: - return url - return url + "?" + parse.urlencode(params) +# TODO: remove this function if everything works +# def get_search_setup(schema: dict): +# """ +# Create a query setup for QueryBuilder to interface the DB. +# +# :param dict schema: pipestat schema +# :return list[dict]: a list dictionaries with search setup to use +# in QueryBuilder +# """ +# setup_dicts = [] +# for col_name, col_schema in schema.items(): +# try: +# setup_dict = { +# "id": col_name, +# "label": col_schema["description"], +# "type": TYPES_MAPPING[col_schema["type"]], +# "validation": VALIDATIONS_MAPPING[col_schema["type"]], +# "operators": OPERATORS_MAPPING[col_schema["type"]], +# } +# except (AttributeError, KeyError): +# _LOGGER.warning( +# f"Database column '{col_name}' of type " +# f"'{col_schema['type']}' has no query builder " +# f"settings predefined, skipping." +# ) +# else: +# setup_dicts.append(setup_dict) +# return setup_dicts +# +# +# def construct_search_data(hits: list, request) -> List[List[str]]: +# """ +# Construct a list of links to display as the search result +# +# :param Iterable[str] hits: ids to compose the list for +# :param starlette.requests.Request request: request for the context +# :return Iterable[list]: results to display +# """ +# template_data = [] +# for h in hits: +# bed_data_url_template = ( +# request.url_for("bedfile") + f"?md5sum={h['md5sum']}&format=" +# ) +# template_data.append( +# [h["name"]] +# + [bed_data_url_template + ext for ext in ["html", "bed", "json"]] +# ) +# return template_data +# +# +# def get_mounted_symlink_path(symlink: str) -> str: +# """ +# Get path to the symlinks target on a mounted filesystem volume. +# Accounts for both transformed and non-transformed symlink targets +# +# :param str symlink: absolute symlink path +# :return str: path to the symlink target on the mounted volume +# """ +# +# def _find_mount_point(path): +# path = os.path.abspath(path) +# while not os.path.ismount(path): +# path = os.path.dirname(path) +# return path +# +# link_tgt = os.readlink(symlink) +# if not os.path.isabs(link_tgt): +# _LOGGER.debug("Volume mounted with symlinks transformation") +# return os.path.abspath(os.path.join(os.path.dirname(symlink), link_tgt)) +# mnt_point = _find_mount_point(symlink) +# first = os.path.relpath(symlink, mnt_point).split("/")[0] +# common_idx = link_tgt.split("/").index(first) +# rel_tgt = os.path.join(*link_tgt.split("/")[common_idx:]) +# return os.path.join(mnt_point, rel_tgt) +# +# +# def get_all_bedset_urls_mapping(bbc: BedBaseConf, request): +# """ +# Get a mapping of all bedset ids and corrsponding splaspages urls +# +# :param bbconf.BedBaseConf bbc: bedbase configuration object +# :param starlette.requests.Request request: request context for url generation +# :return Mapping: a mapping of bedset ids and the urls to the corresponding splashpages +# """ +# hits = bbc.bedset.backend.select(columns=["name", "md5sum"]) +# if not hits: +# return +# # TODO: don't hardcode url path element name, use operationID? +# return { +# hit.name: get_param_url(request.url_for("bedsetsplash"), {"md5sum": hit.md5sum}) +# for hit in hits +# } + + +# def get_param_url(url, params): +# """ +# Create parametrized URL +# +# :param str url: URL base to parametrize +# :param Mapping params: a mapping of URL parameters and values +# :return str: parametrized URL +# """ +# if not params: +# return url +# return url + "?" + parse.urlencode(params) def get_openapi_version(app): diff --git a/requirements/requirements-all.txt b/requirements/requirements-all.txt index 73888b99..1f0b0c93 100644 --- a/requirements/requirements-all.txt +++ b/requirements/requirements-all.txt @@ -1,4 +1,4 @@ -bbconf>=0.4.0 +bbconf>=0.4.1 fastapi>=0.103.0 geniml>=0.1.0 logmuse>=0.2.7 @@ -6,5 +6,4 @@ markdown requests setuptools uvicorn -yacman>=0.9.2 -markdown \ No newline at end of file +yacman>=0.9.2 \ No newline at end of file