diff --git a/bedhost/__init__.py b/bedhost/__init__.py index 8eb6e03..b0a0bf0 100644 --- a/bedhost/__init__.py +++ b/bedhost/__init__.py @@ -1,5 +1,7 @@ -import logmuse import logging + +import logmuse + from .const import PKG_NAME _LOGGER = logmuse.init_logger(PKG_NAME) diff --git a/bedhost/_version.py b/bedhost/_version.py index 49e0fc1..777f190 100644 --- a/bedhost/_version.py +++ b/bedhost/_version.py @@ -1 +1 @@ -__version__ = "0.7.0" +__version__ = "0.8.0" diff --git a/bedhost/cli.py b/bedhost/cli.py index d5a9f42..ce5e7c0 100644 --- a/bedhost/cli.py +++ b/bedhost/cli.py @@ -1,6 +1,7 @@ +from ubiquerg import VersionInHelpParser + from . import PKG_NAME from ._version import __version__ -from ubiquerg import VersionInHelpParser def build_parser(): diff --git a/bedhost/data_models.py b/bedhost/data_models.py index 1fe9fe3..cb524eb 100644 --- a/bedhost/data_models.py +++ b/bedhost/data_models.py @@ -1,6 +1,7 @@ +from enum import Enum + from fastapi import Path from pydantic import BaseModel -from enum import Enum RemoteClassEnum = Enum( "RemoteClassEnum", @@ -69,3 +70,10 @@ class ServiceInfoResponse(BaseModel): version: str component_versions: ComponentVersions embedding_models: EmbeddingModels + + +class BaseListResponse(BaseModel): + count: int + limit: int + offset: int + results: list diff --git a/bedhost/helpers.py b/bedhost/helpers.py index 363413f..1edbd1c 100644 --- a/bedhost/helpers.py +++ b/bedhost/helpers.py @@ -1,7 +1,7 @@ -from starlette.responses import FileResponse, RedirectResponse, JSONResponse +import os from bbconf.bbagent import BedBaseAgent -import os +from starlette.responses import FileResponse, JSONResponse, RedirectResponse from . import _LOGGER from .exceptions import BedHostException @@ -50,7 +50,7 @@ def attach_routers(app): _LOGGER.info("Mounting routers...") # importing routers here avoids circular imports - from .routers import bed_api, bedset_api, objects_api, base_api + from .routers import base_api, bed_api, bedset_api, objects_api app.include_router(base_api.router) app.include_router(bed_api.router) diff --git a/bedhost/main.py b/bedhost/main.py index b124b88..e5f24e7 100644 --- a/bedhost/main.py +++ b/bedhost/main.py @@ -1,34 +1,24 @@ import os import sys -import uvicorn - -from fastapi import FastAPI, Request -from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import HTMLResponse, RedirectResponse import markdown -from fastapi.templating import Jinja2Templates +import uvicorn from bbconf.exceptions import ( - MissingObjectError, - MissingThumbnailError, BEDFileNotFoundError, BedSetNotFoundError, + MissingObjectError, + MissingThumbnailError, ) - +from fastapi import FastAPI, Request +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.templating import Jinja2Templates from . import _LOGGER -from .helpers import ( - configure, - attach_routers, - drs_response, -) -from .cli import build_parser from ._version import __version__ as bedhost_version -from .const import ( - PKG_NAME, - STATIC_PATH, -) - +from .cli import build_parser +from .const import PKG_NAME, STATIC_PATH +from .helpers import attach_routers, configure, drs_response tags_metadata = [ { diff --git a/bedhost/routers/base_api.py b/bedhost/routers/base_api.py index 30596b7..307662c 100644 --- a/bedhost/routers/base_api.py +++ b/bedhost/routers/base_api.py @@ -1,26 +1,27 @@ try: - from typing import Annotated, Dict, Optional, List, Any + from typing import Annotated, Any, Dict, List, Optional except ImportError: from typing_extensions import Annotated from typing import Dict, Optional, List, Any -from fastapi import APIRouter - -from bbconf.models.base_models import StatsReturn from platform import python_version + from bbconf import __version__ as bbconf_version +from bbconf.models.base_models import StatsReturn +from fastapi import APIRouter from geniml import __version__ as geniml_version -from ..main import bbagent, app -from ..helpers import get_openapi_version +from .._version import __version__ as bedhost_version from ..data_models import ( - ServiceInfoResponse, - Type, - Organization, + BaseListResponse, ComponentVersions, EmbeddingModels, + Organization, + ServiceInfoResponse, + Type, ) -from .._version import __version__ as bedhost_version +from ..helpers import get_openapi_version +from ..main import app, bbagent router = APIRouter(prefix="/v1", tags=["base"]) @@ -39,6 +40,24 @@ async def get_bedbase_db_stats(): return bbagent.get_stats() +@router.get( + "/genomes", + summary="Get available genomes", + response_model=BaseListResponse, +) +async def get_bedbase_db_stats(): + """ + Returns statistics + """ + genomes = bbagent.get_list_genomes() + return BaseListResponse( + count=len(genomes), + limit=100, + offset=0, + results=genomes, + ) + + @router.get( "/service-info", summary="GA4GH service info", response_model=ServiceInfoResponse ) diff --git a/bedhost/routers/bed_api.py b/bedhost/routers/bed_api.py index e21c3b0..67e8d85 100644 --- a/bedhost/routers/bed_api.py +++ b/bedhost/routers/bed_api.py @@ -1,43 +1,41 @@ import subprocess try: - from typing import Annotated, Dict, Optional, List, Any + from typing import Annotated, Any, Dict, List, Optional except ImportError: from typing_extensions import Annotated from typing import Dict, Optional, List, Any -from fastapi import APIRouter, HTTPException, Query, UploadFile, File -from fastapi.responses import PlainTextResponse - -from gtars.tokenizers import RegionSet - -import tempfile import os import shutil +import tempfile +from bbconf.exceptions import ( + BedBaseConfError, + BEDFileNotFoundError, + TokenizeFileNotExistError, +) from bbconf.models.bed_models import ( + BedClassification, # BedPEPHub, + BedEmbeddingResult, + BedFiles, BedListResult, + BedListSearchResult, BedMetadataAll, - BedFiles, - BedStatsModel, - BedPlots, - BedClassification, - # BedPEPHub, BedPEPHubRestrict, - BedListSearchResult, - TokenizedPathResponse, + BedPlots, + BedStatsModel, TokenizedBedResponse, - BedEmbeddingResult, + TokenizedPathResponse, ) -from bbconf.exceptions import BEDFileNotFoundError, TokenizeFileNotExistError +from fastapi import APIRouter, File, HTTPException, Query, UploadFile +from fastapi.responses import PlainTextResponse +from gtars.tokenizers import RegionSet from .. import _LOGGER -from ..main import bbagent -from ..data_models import ( - BedDigest, - CROM_NUMBERS, -) from ..const import EXAMPLE_BED +from ..data_models import CROM_NUMBERS, BaseListResponse, BedDigest +from ..main import bbagent router = APIRouter(prefix="/v1/bed", tags=["bed"]) @@ -52,7 +50,7 @@ async def get_example_bed_record(): """ Get metadata for an example BED record. """ - result = bbagent.bed.get_ids_list(limit=1, offset=0).results + result = bbagent.bed.get_ids_list(limit=1, offset=0, genome="hg38").results if result: return result[0] raise HTTPException(status_code=404, detail="No records found") @@ -238,6 +236,33 @@ async def embed_bed_file( return embedding.tolist()[0] +@router.get( + "/missing_plots", + summary="Get missing plots for a bed file.", + response_model=BaseListResponse, +) +async def missing_plots(plot_id: str): + """ + Get missing plots for a bed file + + example -> plot_id: gccontent + """ + + try: + bed_ids = bbagent.bed.get_missing_plots(plot_id, limit=100000, offset=0) + except BedBaseConfError as e: + raise HTTPException( + status_code=404, + detail=f"{e}", + ) + return BaseListResponse( + count=len(bed_ids), + limit=100000, + offset=0, + results=bed_ids, + ) + + @router.get( "/{bed_id}/regions/{chr_num}", summary="Get regions from a BED file that overlap a query region.", diff --git a/bedhost/routers/bedset_api.py b/bedhost/routers/bedset_api.py index 80e9591..bcf05d3 100644 --- a/bedhost/routers/bedset_api.py +++ b/bedhost/routers/bedset_api.py @@ -1,20 +1,19 @@ -from fastapi import APIRouter, HTTPException, Request, Response import logging +from bbconf.exceptions import BedSetNotFoundError from bbconf.models.bedset_models import ( - BedSetMetadata, - BedSetListResult, BedSetBedFiles, + BedSetListResult, + BedSetMetadata, BedSetPlots, BedSetStats, ) -from bbconf.exceptions import BedSetNotFoundError +from fastapi import APIRouter, HTTPException, Request, Response +from ..const import EXAMPLE_BEDSET, PKG_NAME from ..main import bbagent -from ..const import PKG_NAME, EXAMPLE_BEDSET from ..utils import zip_pep - router = APIRouter(prefix="/v1/bedset", tags=["bedset"]) _LOGGER = logging.getLogger(PKG_NAME) diff --git a/bedhost/routers/objects_api.py b/bedhost/routers/objects_api.py index 4bcf19f..157aba9 100644 --- a/bedhost/routers/objects_api.py +++ b/bedhost/routers/objects_api.py @@ -1,17 +1,16 @@ try: - from typing import Annotated, Dict, Optional, List, Any + from typing import Annotated, Any, Dict, List, Optional except ImportError: from typing_extensions import Annotated from typing import Dict, Optional, List, Any -from fastapi import APIRouter, HTTPException, Request +from urllib.parse import urlparse + from bbconf.models.drs_models import DRSModel +from fastapi import APIRouter, HTTPException, Request +from ..helpers import serve_file from ..main import bbagent -from ..helpers import ( - serve_file, -) -from urllib.parse import urlparse router = APIRouter(prefix="/v1/objects", tags=["objects"]) diff --git a/deployment/config/api-dev.bedbase.org.yaml b/deployment/config/api-dev.bedbase.org.yaml index 447c6d9..95c91b4 100644 --- a/deployment/config/api-dev.bedbase.org.yaml +++ b/deployment/config/api-dev.bedbase.org.yaml @@ -14,7 +14,7 @@ qdrant: host: $QDRANT_HOST port: 6333 api_key: $QDRANT_API_KEY - file_collection: bedbase2 + file_collection: bedbase text_collection: bed_text server: host: 0.0.0.0 diff --git a/deployment/config/api.bedbase.org.yaml b/deployment/config/api.bedbase.org.yaml index 0bcb491..5387d61 100644 --- a/deployment/config/api.bedbase.org.yaml +++ b/deployment/config/api.bedbase.org.yaml @@ -14,7 +14,7 @@ qdrant: host: $QDRANT_HOST port: 6333 api_key: $QDRANT_API_KEY - file_collection: bedbase2 + file_collection: bedbase text_collection: bed_text server: host: 0.0.0.0 diff --git a/requirements/requirements-all.txt b/requirements/requirements-all.txt index 7e97817..46423aa 100644 --- a/requirements/requirements-all.txt +++ b/requirements/requirements-all.txt @@ -1,5 +1,5 @@ -#bbconf @ git+https://github.com/databio/bbconf.git@dev#egg=bbconf -bbconf>=0.8.0 +# bbconf @ git+https://github.com/databio/bbconf.git@dev#egg=bbconf +bbconf>=0.9.0 fastapi>=0.103.0 logmuse>=0.2.7 markdown diff --git a/tests/test_fastapi/test_api.py b/tests/test_fastapi/test_api.py index 87c20ec..e744498 100644 --- a/tests/test_fastapi/test_api.py +++ b/tests/test_fastapi/test_api.py @@ -1,6 +1,7 @@ -from fastapi.testclient import TestClient import os + import pytest +from fastapi.testclient import TestClient # os.environ.setdefault("BEDBASE_CONFIG", os.path.abspath("test_config.yaml")) # from bedhost.main import app diff --git a/ui/bedbase-types.d.ts b/ui/bedbase-types.d.ts index be646a0..410d2cb 100644 --- a/ui/bedbase-types.d.ts +++ b/ui/bedbase-types.d.ts @@ -1182,7 +1182,7 @@ export interface components { * @description Name of species. e.g. Homo sapiens. * @default */ - organism: string; + species_name: string; /** * Species Id * @default @@ -1234,7 +1234,7 @@ export interface components { * @description Experimental protocol (e.g. ChIP-seq) * @default */ - exp_protocol: string; + assay: string; /** * Antibody * @description Antibody used in the assay diff --git a/ui/src/components/bedset-splash-components/beds-table.tsx b/ui/src/components/bedset-splash-components/beds-table.tsx index d381b0b..69e2e00 100644 --- a/ui/src/components/bedset-splash-components/beds-table.tsx +++ b/ui/src/components/bedset-splash-components/beds-table.tsx @@ -15,7 +15,6 @@ import { import { useState } from 'react'; import { useBedCart } from '../../contexts/bedcart-context'; import { components } from '../../../bedbase-types'; - type Bed = components['schemas']['BedSetBedFiles']['results'][number]; type Props = { @@ -39,44 +38,59 @@ export const BedsTable = (props: Props) => { const { addBedToCart, removeBedFromCart, cart } = useBedCart(); const columns = [ - columnHelper.accessor('genome_alias', { + columnHelper.accessor((row) => row.genome_alias, { cell: (info) => {info.getValue()}, footer: (info) => info.column.id, header: 'Genome', id: 'genome', }), - columnHelper.accessor('bed_type', { + columnHelper.accessor((row) => row.bed_type, { cell: (info) => {info.getValue()}, footer: (info) => info.column.id, header: 'Type', id: 'bed-type', }), - columnHelper.accessor('name', { + columnHelper.accessor((row) => row.name, { cell: (info) => {info.getValue()}, footer: (info) => info.column.id, header: 'Name', id: 'name', }), - columnHelper.accessor('description', { + columnHelper.accessor((row) => row.annotation?.tissue, { + cell: (info) => {info.getValue()}, + footer: (info) => info.column.id, + header: 'Tissue', + id: 'Tissue', + }), + columnHelper.accessor((row) => row.annotation?.cell_type, { cell: (info) => ( - - {info.getValue() || No description} - + {info.getValue() || } ), footer: (info) => info.column.id, - header: 'Description', - id: 'description', + header: 'Cell Type', + id: 'cell-type', + }), + columnHelper.accessor((row) => row.annotation?.cell_line, { + cell: (info) => {info.getValue()}, + footer: (info) => info.column.id, + header: 'Cell Line', + id: 'cell-line', }), - columnHelper.accessor('id', { - cell: (info) => {info.getValue()}, + columnHelper.accessor((row) => row.description, { + cell: (info) => {info.getValue()}, footer: (info) => info.column.id, - header: 'Record Identifier', - id: 'record-identifier', + header: 'Description', + id: 'description', }), - columnHelper.accessor('id', { + columnHelper.accessor((row) => row.id, { cell: (info) => ( -
+
{ + e.stopPropagation(); + }} + > {!cart.includes(info.getValue()) || (addedToCart && justAddedToCart === info.getValue()) ? ( ) : (
- +
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( - {table.getRowModel().rows.map((row) => ( - + (window.location.href = `/bed/${row.original.id}`)} + > {row.getVisibleCells().map((cell) => ( - + ))} ))} - {/* - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((header) => ( - - ))} - - ))} - */}
+ {header.isPlaceholder ? null : (
{
{flexRender(cell.column.columnDef.cell, cell.getContext())} + {flexRender(cell.column.columnDef.cell, cell.getContext())} +
- {header.isPlaceholder ? null : flexRender(header.column.columnDef.footer, header.getContext())} -
diff --git a/ui/src/components/bedset-splash-components/cards/gc-content-card.tsx b/ui/src/components/bedset-splash-components/cards/gc-content-card.tsx index ca1b951..9983fcc 100644 --- a/ui/src/components/bedset-splash-components/cards/gc-content-card.tsx +++ b/ui/src/components/bedset-splash-components/cards/gc-content-card.tsx @@ -15,11 +15,11 @@ export const GCContentCard = (props: Props) => {

{metadata.statistics?.mean?.gc_content - ? `${formatNumberWithCommas(Math.round(metadata.statistics?.mean?.gc_content || 0))}} bp` + ? `${formatNumberWithCommas(Math.round((metadata.statistics?.mean?.gc_content || 0)*100)/100)}` : 'N/A'}

{/* plus minus */} -

± {formatNumberWithCommas(Math.round(metadata.statistics?.sd?.gc_content || 0))} bp

+

± {formatNumberWithCommas(Math.round((metadata.statistics?.sd?.gc_content || 0)*100)/100)}

); diff --git a/ui/src/components/search/search-bedset-table.tsx b/ui/src/components/search/search-bedset-table.tsx index b4bdf01..9417728 100644 --- a/ui/src/components/search/search-bedset-table.tsx +++ b/ui/src/components/search/search-bedset-table.tsx @@ -10,7 +10,7 @@ export const SearchBedSetResultTable = (props: Props) => { const { results } = props; return ( - +
@@ -24,14 +24,14 @@ export const SearchBedSetResultTable = (props: Props) => { {results.results?.map((result) => ( - +
Bedset ID
{result?.id || 'Unknown Id'} {result?.name || 'Unknown Name'} {result?.description || 'Unknown Description'} {result?.bed_ids?.length || 0} - - diff --git a/ui/src/components/search/text2bed/t2b-search-results-table.tsx b/ui/src/components/search/text2bed/t2b-search-results-table.tsx index 0e5576c..c5f9765 100644 --- a/ui/src/components/search/text2bed/t2b-search-results-table.tsx +++ b/ui/src/components/search/text2bed/t2b-search-results-table.tsx @@ -16,28 +16,29 @@ export const Text2BedSearchResultsTable = (props: Props) => { const { results } = props; const { cart, addBedToCart, removeBedFromCart } = useBedCart(); return ( - +
- - - - - - - {/**/} - {/**/} - - - - {/* */} - - + + + + + + + {/**/} + {/**/} + + + + + {/* */} + + - {results.results?.map((result) => ( - + {results.results?.map((result) => ( + */} + {/**/} + {/* */} {/**/}
NameGenomeTissueCell LineCell TypeTarget AntibodyDescriptionInfoScoreBEDbase ID - Actions -
NameGenomeTissueCell LineCell TypeTarget AntibodyDescriptionAssayInfoScoreBEDbase ID + Actions +
{result?.metadata?.name || 'No name'} {result?.metadata?.genome_alias || 'N/A'} @@ -49,10 +50,11 @@ export const Text2BedSearchResultsTable = (props: Props) => { {/*{result?.metadata?.annotation?.antibody || 'N/A'}{result?.metadata?.description || ''}{result?.metadata?.annotation?.assay || 'N/A'}
@@ -64,7 +66,7 @@ export const Text2BedSearchResultsTable = (props: Props) => {
                   
                 }
               >
-                
+                
               
             
@@ -75,6 +77,7 @@ export const Text2BedSearchResultsTable = (props: Props) => { variant="primary" /> {result?.metadata?.id || 'No id'}*/} {/* /!*{result?.metadata?.submission_date === undefined*!/*/} @@ -83,14 +86,15 @@ export const Text2BedSearchResultsTable = (props: Props) => { {/* */} {/* - - + + {/**/} {cart.includes(result?.metadata?.id || '') ? ( ) : (