Skip to content

Commit

Permalink
Merge pull request #355 from pepkit/328_models
Browse files Browse the repository at this point in the history
328 models
  • Loading branch information
khoroshevskyi authored Jul 15, 2024
2 parents a3fdac2 + 9b20ecb commit 5e4b714
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 177 deletions.
2 changes: 0 additions & 2 deletions pephub/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from .routers.api.v1.project import project as api_project
from .routers.api.v1.project import projects as api_projects
from .routers.api.v1.search import search as api_search
from .routers.api.v1.user import user as api_user
from .routers.auth.base import auth as auth_router
from .routers.eido.eido import router as eido_router

Expand Down Expand Up @@ -78,7 +77,6 @@

# build routes
app.include_router(api_base)
app.include_router(api_user)
app.include_router(api_namespace)
app.include_router(api_namespaces)
app.include_router(api_project)
Expand Down
5 changes: 3 additions & 2 deletions pephub/routers/api/v1/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
from fastapi import APIRouter

from ....const import ALL_VERSIONS
from ...models import BaseEndpointResponseModel, VersionResponseModel

load_dotenv()

api = APIRouter(prefix="/api/v1", tags=["base"])


@api.get("/")
@api.get("/", response_model=BaseEndpointResponseModel)
async def api_base():
"""
Base API endpoint.
Expand All @@ -19,6 +20,6 @@ async def api_base():
}


@api.get("/_version")
@api.get("/_version", response_model=VersionResponseModel)
async def version():
return dict(**ALL_VERSIONS)
109 changes: 49 additions & 60 deletions pephub/routers/api/v1/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@
@namespace.get(
"/",
summary="Fetch details about a particular namespace.",
response_model=Namespace,
)
async def get_namespace(
request: Request,
nspace: Namespace = Depends(get_namespace_info),
namespace_info: Namespace = Depends(get_namespace_info),
):
"""
Fetch namespace. Returns a JSON representation of the namespace.
Expand All @@ -67,9 +67,7 @@ async def get_namespace(
namespace: databio
"""
nspace = nspace.model_dump()
nspace["projects_endpoint"] = f"{str(request.url)[:-1]}/projects"
return JSONResponse(content=nspace)
return namespace_info


@namespace.get(
Expand All @@ -83,8 +81,7 @@ async def get_namespace_projects(
limit: int = 10,
offset: int = 0,
query: str = None,
session_info: dict = Depends(read_authorization_header),
namespace_access: List[str] = Depends(get_namespace_access_list),
admin_list: List[str] = Depends(get_namespace_access_list),
order_by: str = "update_date",
order_desc: bool = False,
filter_by: Annotated[
Expand Down Expand Up @@ -112,7 +109,7 @@ async def get_namespace_projects(
namespace=namespace,
limit=limit,
offset=offset,
admin=namespace_access,
admin=admin_list,
order_by=order_by,
order_desc=order_desc,
filter_by=filter_by,
Expand All @@ -125,25 +122,16 @@ async def get_namespace_projects(
namespace=namespace,
limit=limit,
offset=offset,
admin=namespace_access,
admin=admin_list,
order_by=order_by,
order_desc=order_desc,
filter_by=filter_by,
filter_start_date=filter_start_date,
filter_end_date=filter_end_date,
pep_type=pep_type,
)
results = [p.model_dump() for p in search_result.results]

return JSONResponse(
content={
"count": search_result.count,
"limit": limit,
"offset": offset,
"items": results,
"session_info": session_info,
}
)
return search_result


# url format based on:
Expand All @@ -170,8 +158,8 @@ async def create_pep(
Create a PEP for a particular namespace you have write access to.
Don't know your namespace? Log in to see.
"""

if files is not None:
init_file = parse_user_file_upload(files)
init_file, other_files = split_upload_files_on_init_file(files, init_file)
Expand Down Expand Up @@ -212,54 +200,55 @@ async def create_pep(
content={
"namespace": namespace,
"name": name,
"proj": p.to_dict(),
"init_file": init_file.filename,
"tag": tag,
"registry_path": f"{namespace}/{name}:{tag}",
},
status_code=202,
)
# create a blank peppy.Project object with fake files
else:
# create temp dir that gets deleted when we're done
with tempfile.TemporaryDirectory() as dirpath:
config_file_name = "project_config.yaml"
sample_table_name = BLANK_PEP_CONFIG["sample_table"]

# create 'empty' config and sample table files
with open(f"{dirpath}/{config_file_name}", "w") as cfg_fh:
cfg_fh.write(json.dumps(BLANK_PEP_CONFIG))
with open(f"{dirpath}/{sample_table_name}", "w") as cfg_fh:
cfg_fh.write(BLANK_PEP_SAMPLE_TABLE)
raise HTTPException(
detail=f"Project files were not provided",
status_code=400,
)

# init project
p = Project(f"{dirpath}/{config_file_name}")
p.name = name
p.description = description
p.pep_schema = pep_schema
try:
agent.project.create(
p,
namespace=namespace,
name=name,
tag=tag,
is_private=is_private,
)
except ProjectUniqueNameError as _:
raise HTTPException(
detail=f"Project '{namespace}/{p.name}:{tag}' already exists in namespace",
status_code=400,
)
return JSONResponse(
content={
"namespace": namespace,
"name": name,
"proj": p.to_dict(),
"tag": tag,
"registry_path": f"{namespace}/{name}:{tag}",
},
status_code=202,
)
# # create temp dir that gets deleted when we're done
# with tempfile.TemporaryDirectory() as dirpath:
# config_file_name = "project_config.yaml"
# sample_table_name = BLANK_PEP_CONFIG["sample_table"]
#
# # create 'empty' config and sample table files
# with open(f"{dirpath}/{config_file_name}", "w") as cfg_fh:
# cfg_fh.write(json.dumps(BLANK_PEP_CONFIG))
# with open(f"{dirpath}/{sample_table_name}", "w") as cfg_fh:
# cfg_fh.write(BLANK_PEP_SAMPLE_TABLE)
#
# # init project
# p = Project(f"{dirpath}/{config_file_name}")
# try:
# agent.project.create(
# p,
# namespace=namespace,
# name=name,
# tag=tag,
# description=description,
# is_private=is_private,
# pep_schema=pep_schema,
# )
# except ProjectUniqueNameError as _:
# raise HTTPException(
# detail=f"Project '{namespace}/{p.name}:{tag}' already exists in namespace",
# status_code=400,
# )
# return JSONResponse(
# content={
# "namespace": namespace,
# "name": name,
# "tag": tag,
# "registry_path": f"{namespace}/{name}:{tag}",
# },
# status_code=202,
# )


@namespace.post(
Expand Down
54 changes: 24 additions & 30 deletions pephub/routers/api/v1/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from dotenv import load_dotenv
from fastapi import APIRouter, Body, Depends, Query
from fastapi.exceptions import HTTPException
from fastapi.responses import JSONResponse, PlainTextResponse
from fastapi.responses import JSONResponse, PlainTextResponse, FileResponse
from pepdbagent import PEPDatabaseAgent
from pepdbagent.exceptions import (
ProjectNotFoundError,
Expand Down Expand Up @@ -51,6 +51,8 @@
ProjectRawModel,
ProjectRawRequest,
ProjectHistoryResponse,
SamplesResponseModel,
ConfigResponseModel,
)
from .helpers import verify_updated_project

Expand Down Expand Up @@ -217,7 +219,7 @@ async def delete_a_pep(
)


@project.get("/samples")
@project.get("/samples", response_model=SamplesResponseModel)
async def get_pep_samples(
proj: dict = Depends(get_project),
format: Optional[str] = None,
Expand Down Expand Up @@ -245,25 +247,21 @@ async def get_pep_samples(
else:
if raw:
df = pd.DataFrame(proj[SAMPLE_RAW_DICT_KEY])
return JSONResponse(
{
"count": df.shape[0],
"items": df.replace({np.nan: None}).to_dict(orient="records"),
}
return SamplesResponseModel(
count=df.shape[0],
items=df.replace({np.nan: None}).to_dict(orient="records"),
)
else:
return JSONResponse(
{
"count": len(proj.samples),
"items": [s.to_dict() for s in proj.samples],
}
proj = peppy.Project.from_dict(proj)
return SamplesResponseModel(
count=len(proj.samples),
items=[s.to_dict() for s in proj.samples],
)


@project.get("/config", summary="Get project configuration file")
async def get_pep_config(
config: dict = Depends(get_config),
# format: Optional[Literal["JSON", "String"]] = "JSON",
):
"""
Get project configuration file from a certain project and namespace
Expand All @@ -274,10 +272,8 @@ async def get_pep_config(
namespace: databio
tag: default
"""
return JSONResponse(
{
"config": yaml.dump(config, sort_keys=False),
}
return ConfigResponseModel(
config=config,
)


Expand Down Expand Up @@ -463,7 +459,7 @@ async def delete_sample(
)


@project.get("/subsamples")
@project.get("/subsamples", response_model=SamplesResponseModel)
async def get_subsamples_endpoint(
subsamples: peppy.Project = Depends(get_subsamples),
download: bool = False,
Expand All @@ -489,19 +485,15 @@ async def get_subsamples_endpoint(
if download:
return subsamples.to_csv()
else:
return JSONResponse(
{
"count": subsamples.shape[0],
"items": subsamples.to_dict(orient="records"),
}
return SamplesResponseModel(
count=subsamples.shape[0],
items=subsamples.to_dict(orient="records"),
)

else:
return JSONResponse(
{
"count": 0,
"items": [],
}
return SamplesResponseModel(
count=0,
items=[],
)


Expand Down Expand Up @@ -552,7 +544,7 @@ async def convert_pep(
return resp_obj


@project.get("/zip")
@project.get("/zip", response_class=FileResponse)
async def zip_pep_for_download(proj: Dict[str, Any] = Depends(get_project)):
"""
Zip a pep
Expand Down Expand Up @@ -618,7 +610,7 @@ async def fork_pep_to_namespace(
)


@project.get("/annotation")
@project.get("/annotation", response_model=AnnotationModel)
async def get_project_annotation(
proj_annotation: AnnotationModel = Depends(get_project_annotation),
):
Expand Down Expand Up @@ -752,6 +744,7 @@ async def create_view_of_the_project(
"/views/{view}/zip",
summary="Zip a view",
tags=["views"],
response_class=FileResponse,
)
async def zip_view_of_the_view(
namespace: str,
Expand Down Expand Up @@ -1069,6 +1062,7 @@ def restore_project_history_by_id(
@project.get(
"/history/{history_id}/zip",
summary="Zip a project history by id",
response_class=FileResponse,
)
def get_zip_snapshot(
namespace: str,
Expand Down
19 changes: 6 additions & 13 deletions pephub/routers/api/v1/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from fastapi.responses import JSONResponse
from fastembed.embedding import FlagEmbedding as Embedding
from pepdbagent import PEPDatabaseAgent
from pepdbagent.models import ListOfNamespaceInfo
from pepdbagent.models import NamespaceList
from qdrant_client import QdrantClient

from ....const import DEFAULT_QDRANT_COLLECTION_NAME
Expand All @@ -22,23 +22,16 @@
search = APIRouter(prefix="/api/v1/search", tags=["search"])


@search.get("/namespaces", summary="Search for namespaces")
@search.get(
"/namespaces", summary="Search for namespaces", response_model=NamespaceList
)
async def search_for_namespaces(
limit: Optional[int] = 1_000,
query: Optional[str] = "",
offset: Optional[int] = 0,
agent: PEPDatabaseAgent = Depends(get_db),
) -> ListOfNamespaceInfo:
res = agent.namespace.get(limit=limit, query=query or "", offset=offset)

return JSONResponse(
content={
"results": [r.model_dump() for r in res.results],
"count": res.count,
"limit": limit,
"offset": offset,
}
)
) -> NamespaceList:
return agent.namespace.get(limit=limit, query=query or "", offset=offset)


# perform a search
Expand Down
Loading

0 comments on commit 5e4b714

Please sign in to comment.