Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added views endpoints #259

Merged
merged 6 commits into from
Jan 12, 2024
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
2 changes: 1 addition & 1 deletion pephub/routers/api/v1/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ async def create_pep(
tag: str = Form(DEFAULT_TAG),
description: Union[str, None] = Form(None),
pep_schema: str = Form(DEFAULT_PEP_SCHEMA),
files: Optional[List[UploadFile]] = File(
files: List[UploadFile] = File(
None # let the file upload be optional. dont send a file? We instantiate with blank
),
agent: PEPDatabaseAgent = Depends(get_db),
Expand Down
273 changes: 271 additions & 2 deletions pephub/routers/api/v1/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import yaml
import pandas as pd
import peppy
import logging
from typing import Callable, Literal, Union, Optional, List, Annotated
from fastapi import APIRouter, Depends, Query, Body
from fastapi.exceptions import HTTPException
Expand All @@ -17,8 +18,17 @@
)

from pepdbagent import PEPDatabaseAgent
from pepdbagent.exceptions import ProjectUniqueNameError
from pepdbagent.models import AnnotationModel, AnnotationList
from pepdbagent.exceptions import (
ProjectUniqueNameError,
SampleAlreadyInView,
SampleNotFoundError,
)
from pepdbagent.models import (
AnnotationModel,
AnnotationList,
CreateViewDictModel,
ProjectViews,
)

from dotenv import load_dotenv

Expand All @@ -36,6 +46,7 @@
)
from ....const import SAMPLE_CONVERSION_FUNCTIONS, VALID_UPDATE_KEYS

_LOGGER = logging.getLogger(__name__)

load_dotenv()

Expand Down Expand Up @@ -694,6 +705,8 @@ async def fork_pep_to_namespace(
fork_request: ForkRequest,
proj_annotation: AnnotationModel = Depends(get_project_annotation),
agent: PEPDatabaseAgent = Depends(get_db),
description: Optional[str] = "",
private: Optional[bool] = False,
):
"""
Fork a project for a particular namespace you have write access to.
Expand All @@ -712,6 +725,8 @@ async def fork_pep_to_namespace(
fork_namespace=fork_to,
fork_name=fork_name,
fork_tag=fork_tag,
description=description or proj_annotation.description,
private=private or proj_annotation.is_private,
)

except ProjectUniqueNameError as _:
Expand Down Expand Up @@ -739,3 +754,257 @@ async def get_project_annotation(
Get project annotation from a certain project and namespace
"""
return proj_annotation


#### Views ####
@project.get(
"/views",
summary="get list of views for a project",
tags=["views"],
response_model=ProjectViews,
)
def get_views(
namespace: str,
project: str,
tag: str = DEFAULT_TAG,
agent: PEPDatabaseAgent = Depends(get_db),
):
return agent.view.get_views_annotation(namespace, project, tag=tag)


@project.get(
"/views/{view}",
summary="Fetch a project view",
response_model=Union[ProjectRawModel, dict],
tags=["views"],
)
async def get_view_of_the_project(
namespace: str,
project: str,
view: str,
tag: str = DEFAULT_TAG,
raw: bool = True,
agent: PEPDatabaseAgent = Depends(get_db),
):
"""
Fetch a view of the project.
"""
if raw:
return ProjectRawModel(
**agent.view.get(
namespace=namespace,
name=project,
view_name=view,
tag=tag,
raw=raw,
)
)
else:
return agent.view.get(
namespace=namespace,
name=project,
view_name=view,
tag=tag,
raw=raw,
).to_dict()


@project.post(
"/views/{view}",
summary="Create a view",
tags=["views"],
)
async def create_view_of_the_project(
namespace: str,
project: str,
view: str,
tag: str = DEFAULT_TAG,
description: str = "",
sample_names: List[str] = None,
namespace_access_list: List[str] = Depends(get_namespace_access_list),
agent: PEPDatabaseAgent = Depends(get_db),
):
"""
Create a view of the project.
"""
if namespace not in namespace_access_list:
raise HTTPException(
detail="You do not have permission to create this view.",
status_code=401,
)
try:
agent.view.create(
view_name=view,
description=description,
view_dict=CreateViewDictModel(
project_namespace=namespace,
project_name=project,
project_tag=tag,
sample_list=sample_names,
),
)
except Exception as e:
_LOGGER.error(f"Could not create view. Error: {e}")
raise HTTPException(
status_code=400,
detail=f"Could not create view. Server error",
)
return JSONResponse(
content={
"message": "View created successfully.",
"registry": f"{namespace}/{project}:{tag}",
},
status_code=202,
)


@project.get(
"/views/{view}/zip",
summary="Zip a view",
tags=["views"],
)
async def zip_view_of_the_view(
namespace: str,
project: str,
view: str,
tag: str = DEFAULT_TAG,
agent: PEPDatabaseAgent = Depends(get_db),
):
"""
Zip a view of the project.
"""
return zip_pep(
agent.view.get(
namespace=namespace,
name=project,
view_name=view,
tag=tag,
raw=False,
)
)


@project.post(
"/views/{view}/{sample_name}",
summary="Add sample to the view",
tags=["views"],
)
async def add_sample_to_view(
namespace: str,
project: str,
view: str,
sample_name: str,
tag: str = DEFAULT_TAG,
namespace_access_list: List[str] = Depends(get_namespace_access_list),
agent: PEPDatabaseAgent = Depends(get_db),
):
if namespace not in namespace_access_list:
raise HTTPException(
detail="You do not have permission to add sample to this view.",
status_code=401,
)
try:
agent.view.add_sample(
namespace=namespace,
name=project,
tag=tag,
view_name=view,
sample_name=sample_name,
)
except SampleAlreadyInView:
raise HTTPException(
status_code=400,
detail=f"Sample '{sample_name}' already in view '{view}'",
)
except SampleNotFoundError:
raise HTTPException(
status_code=400,
detail=f"Sample '{sample_name}' not found in project '{namespace}/{project}:{tag}'",
)
return JSONResponse(
content={
"message": "Sample added to view successfully.",
"registry": f"{namespace}/{project}:{tag}",
},
status_code=202,
)


@project.delete(
"/views/{view}/{sample_name}",
summary="Delete sample from the view",
tags=["views"],
)
def delete_sample_from_view(
namespace: str,
project: str,
view: str,
sample_name: str,
tag: str = DEFAULT_TAG,
namespace_access_list: List[str] = Depends(get_namespace_access_list),
agent: PEPDatabaseAgent = Depends(get_db),
):
if namespace not in namespace_access_list:
raise HTTPException(
detail="You do not have permission to delete sample from this view.",
status_code=401,
)
try:
agent.view.remove_sample(
namespace=namespace,
name=project,
tag=tag,
view_name=view,
sample_name=sample_name,
)
except SampleNotFoundError:
raise HTTPException(
status_code=400,
detail=f"Sample '{sample_name}' not found in view '{view}'",
)
return JSONResponse(
content={
"message": "Sample deleted from view successfully.",
"registry": f"{namespace}/{project}:{tag}",
},
status_code=202,
)


@project.delete(
"/views/{view}",
summary="Delete a view",
tags=["views"],
)
def delete_view(
namespace: str,
project: str,
view: str,
tag: str = DEFAULT_TAG,
namespace_access_list: List[str] = Depends(get_namespace_access_list),
agent: PEPDatabaseAgent = Depends(get_db),
):
if namespace not in namespace_access_list:
raise HTTPException(
detail="You do not have permission to delete this view.",
status_code=401,
)
try:
agent.view.delete(
project_namespace=namespace,
project_name=project,
project_tag=tag,
view_name=view,
)
except Exception:
raise HTTPException(
status_code=400,
detail=f"Could not delete view. Server error!",
)
return JSONResponse(
content={
"message": "View deleted successfully.",
"registry": f"{namespace}/{project}:{tag}",
},
status_code=202,
)
Loading