Skip to content

Commit

Permalink
Merge pull request #302 from pepkit/dev
Browse files Browse the repository at this point in the history
Critical bug fixes
  • Loading branch information
khoroshevskyi authored Feb 26, 2024
2 parents 7a6a64c + fc3b1aa commit 367ae24
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 105 deletions.
8 changes: 7 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format.

## [0.11.6] - 02-08-2024
## [0.11.6] - 02-26-2024

- Fix some bugs introduced as a result of the last release:
- tag's were being removed from the URL params when selecting a project view
- stabilized query params along the way on the namespace page

## [0.11.7] - 02-22-2024

- Added interface for selecting and viewing project views
- optimized loading of very large sample tables
Expand Down
2 changes: 1 addition & 1 deletion pephub/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.11.7"
__version__ = "0.11.8"
32 changes: 32 additions & 0 deletions pephub/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,38 @@ def get_project(
)


def get_config(
namespace: str,
project: str,
tag: Optional[str] = DEFAULT_TAG,
agent: PEPDatabaseAgent = Depends(get_db),
) -> Dict[str, Any]:
try:
config = agent.project.get_config(namespace, project, tag)
yield config
except ProjectNotFoundError:
raise HTTPException(
404,
f"PEP '{namespace}/{project}:{tag or DEFAULT_TAG}' does not exist in database. Did you spell it correctly?",
)


def get_subsamples(
namespace: str,
project: str,
tag: Optional[str] = DEFAULT_TAG,
agent: PEPDatabaseAgent = Depends(get_db),
) -> Dict[str, Any]:
try:
subsamples = agent.project.get_subsamples(namespace, project, tag)
yield subsamples
except ProjectNotFoundError:
raise HTTPException(
404,
f"PEP '{namespace}/{project}:{tag or DEFAULT_TAG}' does not exist in database. Did you spell it correctly?",
)


def get_project_annotation(
namespace: str,
project: str,
Expand Down
31 changes: 11 additions & 20 deletions pephub/routers/api/v1/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
from ....dependencies import (
get_db,
get_project,
get_config,
get_subsamples,
get_project_annotation,
get_namespace_access_list,
verify_user_can_fork,
Expand Down Expand Up @@ -413,29 +415,21 @@ async def get_pep_samples(

@project.get("/config", summary="Get project configuration file")
async def get_pep_config(
proj: Union[peppy.Project, dict] = Depends(get_project),
config: dict = Depends(get_config),
format: Optional[Literal["JSON", "String"]] = "JSON",
raw: Optional[bool] = False,
):
"""
Get project configuration file from a certain project and namespace
Don't have a namespace or project?
Use the following:
project: example
namespace: databio
tag: default
"""
if raw:
proj_config = proj[CONFIG_KEY]
else:
proj_config = proj.to_dict(extended=True, orient="records")[CONFIG_KEY]
if format == "JSON":
return JSONResponse(proj_config)
return JSONResponse(
{
"config": yaml.dump(proj_config, sort_keys=False),
"config": yaml.dump(config, sort_keys=False),
}
)

Expand Down Expand Up @@ -622,8 +616,8 @@ async def delete_sample(


@project.get("/subsamples")
async def get_subsamples(
proj: peppy.Project = Depends(get_project),
async def get_subsamples_endpoint(
subsamples: peppy.Project = Depends(get_subsamples),
download: bool = False,
):
"""
Expand All @@ -636,16 +630,11 @@ async def get_subsamples(
project: example
namespace: databio
"""
if isinstance(proj, dict):
subsamples = proj[SUBSAMPLE_RAW_LIST_KEY]
else:
subsamples = proj.to_dict(extended=True, orient="records")[
SUBSAMPLE_RAW_LIST_KEY
]

if subsamples:
try:
subsamples = pd.DataFrame(
proj[SUBSAMPLE_RAW_LIST_KEY][0]
subsamples[0]
) # TODO: update this enpoint, so that it has access to all subsample tables
except IndexError:
subsamples = pd.DataFrame()
Expand Down Expand Up @@ -861,6 +850,7 @@ async def create_view_of_the_project(
tag: str = DEFAULT_TAG,
description: str = "",
sample_names: List[str] = None,
no_fail: bool = False,
namespace_access_list: List[str] = Depends(get_namespace_access_list),
agent: PEPDatabaseAgent = Depends(get_db),
):
Expand All @@ -875,6 +865,7 @@ async def create_view_of_the_project(
try:
agent.view.create(
view_name=view,
no_fail=no_fail,
description=description,
view_dict=CreateViewDictModel(
project_namespace=namespace,
Expand Down
4 changes: 2 additions & 2 deletions requirements/requirements-all.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
fastapi>=0.108.0
psycopg>=3.1.15
pepdbagent @ git+https://github.com/pepkit/pepdbagent.git@dev#egg=pepdbagent
# pepdbagent>=0.7.3
# pepdbagent @ git+https://github.com/pepkit/pepdbagent.git@dev#egg=pepdbagent
pepdbagent>=0.8.0
peppy>=0.40.1
eido>=0.2.2
jinja2>=3.1.2
Expand Down
19 changes: 12 additions & 7 deletions web/src/components/namespace/search-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FC } from 'react';
import { set } from 'react-hook-form';
import { useSearchParams } from 'react-router-dom';

interface Props {
namespace: string;
Expand All @@ -26,6 +27,7 @@ export const NamespacePageSearchBar: FC<Props> = ({
setOrder,
setOffset,
}) => {
const [searchParams, setSearchParams] = useSearchParams();
return (
<div className="flex-row d-flex align-items-center" style={{ position: 'relative' }}>
<div className="input-group shadow-sm">
Expand All @@ -37,6 +39,12 @@ export const NamespacePageSearchBar: FC<Props> = ({
onChange={(e) => {
setSearch(e.target.value);
setOffset(0);
if (e.target.value === '') {
searchParams.delete('search');
} else {
searchParams.set('search', e.target.value);
}
setSearchParams(searchParams);
}}
id="search-bar"
type="text"
Expand All @@ -47,9 +55,8 @@ export const NamespacePageSearchBar: FC<Props> = ({
value={limit}
onChange={(e) => {
setLimit(parseInt(e.target.value));
const urlParams = new URLSearchParams(window.location.search);
urlParams.set('limit', e.target.value);
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
searchParams.set('limit', e.target.value);
setSearchParams(searchParams);
}}
className="form-control form-select"
>
Expand All @@ -64,10 +71,8 @@ export const NamespacePageSearchBar: FC<Props> = ({
const [orderBy, order] = e.target.value.split('+');
setOrderBy(orderBy);
setOrder(order);
const urlParams = new URLSearchParams(window.location.search);
urlParams.set('orderBy', orderBy);
urlParams.set('order', order);
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
searchParams.set('orderBy', orderBy);
setSearchParams(searchParams);
}}
className="form-control form-select"
>
Expand Down
12 changes: 11 additions & 1 deletion web/src/components/namespace/star-filter-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { FC } from 'react';
import { useSearchParams } from 'react-router-dom';

interface Props {
search: string;
setSearch: (search: string) => void;
}

export const StarFilterBar: FC<Props> = ({ search, setSearch }) => {
const [searchParams, setSearchParams] = useSearchParams();
return (
<div className="flex-row d-flex align-items-center" style={{ position: 'relative' }}>
<div className="input-group shadow-sm">
Expand All @@ -14,7 +16,15 @@ export const StarFilterBar: FC<Props> = ({ search, setSearch }) => {
</span>
<input
value={search}
onChange={(e) => setSearch(e.target.value)}
onChange={(e) => {
setSearch(e.target.value);
if (e.target.value === '') {
searchParams.delete('search');
} else {
searchParams.set('search', e.target.value);
}
setSearchParams(searchParams);
}}
id="search-bar"
type="text"
className="form-control w-60"
Expand Down
8 changes: 7 additions & 1 deletion web/src/components/namespace/view-selector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC } from 'react';
import Nav from 'react-bootstrap/Nav';
import { NavLink } from 'react-router-dom';
import { NavLink, useSearchParams } from 'react-router-dom';

type View = 'peps' | 'pops' | 'stars';

Expand All @@ -14,7 +14,13 @@ type Props = {
};

export const NamespaceViewSelector: FC<Props> = (props) => {
const [searchParams, setSearchParams] = useSearchParams();
const handleNavSelect = (eventKey: string | null) => {
if (eventKey === null) {
return;
}
searchParams.set('view', eventKey);
setSearchParams(searchParams);
props.setView(eventKey as View);
};

Expand Down
16 changes: 5 additions & 11 deletions web/src/components/project/view-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useSearchParams } from 'react-router-dom';
import ReactSelect from 'react-select';

import { ProjectViewsResponse } from '../../api/project';
import { search } from '../../api/search';

interface Props {
projectViewsIsLoading: boolean;
Expand Down Expand Up @@ -39,22 +40,15 @@ export const ViewSelector = (props: Props) => {
})) || []
}
onChange={(selectedOption) => {
debugger;
if (selectedOption === null) {
setView(undefined);
searchParams.delete('view');
setSearchParams(
new URLSearchParams({
...searchParams,
}),
);
setSearchParams(searchParams);
} else {
setView(selectedOption.value);
setSearchParams(
new URLSearchParams({
...searchParams,
view: selectedOption.value,
}),
);
searchParams.set('view', selectedOption.value);
setSearchParams(searchParams);
}
}}
isDisabled={projectViews?.views.length === 0 || projectViewsIsLoading}
Expand Down
44 changes: 9 additions & 35 deletions web/src/pages/Namespace.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC, Fragment, useEffect, useState } from 'react';
import Breadcrumb from 'react-bootstrap/Breadcrumb';
import { useParams } from 'react-router-dom';
import { useParams, useSearchParams } from 'react-router-dom';

import { GitHubAvatar } from '../components/badges/github-avatar';
import { PageLayout } from '../components/layout/page-layout';
Expand All @@ -24,9 +24,8 @@ import { numberWithCommas } from '../utils/etc';
type View = 'peps' | 'pops' | 'stars';

export const NamespacePage: FC = () => {
// get view out of url its a query param
const urlParams = new URLSearchParams(window.location.search);
const viewFromUrl = urlParams.get('view') as View;
const [searchParams, setSearchParams] = useSearchParams();
const viewFromUrl = searchParams.get('view') as View;

// get namespace from url
let { namespace } = useParams();
Expand All @@ -36,18 +35,18 @@ export const NamespacePage: FC = () => {
const { user } = useSession();

// pagination
const [limit, setLimit] = useState(urlParams.get('limit') ? parseInt(urlParams.get('limit')!) : 10);
const [offset, setOffset] = useState(urlParams.get('offset') ? parseInt(urlParams.get('offset')!) : 0);
const [search, setSearch] = useState(urlParams.get('search') || '');
const [orderBy, setOrderBy] = useState(urlParams.get('orderBy') || 'update_date');
const [order, setOrder] = useState(urlParams.get('order') || 'asc');
const [limit, setLimit] = useState(searchParams.get('limit') ? parseInt(searchParams.get('limit')!) : 10);
const [offset, setOffset] = useState(searchParams.get('offset') ? parseInt(searchParams.get('offset')!) : 0);
const [search, setSearch] = useState(searchParams.get('search') || '');
const [orderBy, setOrderBy] = useState(searchParams.get('orderBy') || 'update_date');
const [order, setOrder] = useState(searchParams.get('order') || 'asc');

// state
const [showAddPEPModal, setShowAddPEPModal] = useState(false);
const [showEndpointsModal, setShowEndpointsModal] = useState(false);
const [showGeoDownloadModal, setShowGeoDownloadModal] = useState(false);
const [view, setView] = useState<View>(viewFromUrl === 'stars' ? 'stars' : 'peps');
const [starSearch, setStarSearch] = useState<string>(urlParams.get('starSearch') || '');
const [starSearch, setStarSearch] = useState<string>(searchParams.get('starSearch') || '');

const searchDebounced = useDebounce<string>(search, 500);

Expand Down Expand Up @@ -75,31 +74,6 @@ export const NamespacePage: FC = () => {
// left over from when we were filtering on sample number
const projectsFiltered = projects?.items.filter((p) => p.number_of_samples) || [];

// update url when search changes
useEffect(() => {
if (typeof window !== 'undefined' && search === '') {
const urlParams = new URLSearchParams(window.location.search);
urlParams.delete('search');
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
} else if (typeof window !== 'undefined') {
const urlParams = new URLSearchParams(window.location.search);
urlParams.set('search', search);
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
}
}, [search]);

useEffect(() => {
if (typeof window !== 'undefined' && starSearch === '') {
const urlParams = new URLSearchParams(window.location.search);
urlParams.delete('starSearch');
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
} else if (typeof window !== 'undefined') {
const urlParams = new URLSearchParams(window.location.search);
urlParams.set('starSearch', starSearch);
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
}
}, [starSearch]);

if (namespaceInfoIsLoading || starsIsLoading) {
return (
<PageLayout title={namespace}>
Expand Down
Loading

0 comments on commit 367ae24

Please sign in to comment.