Skip to content

Commit 367ae24

Browse files
Merge pull request #302 from pepkit/dev
Critical bug fixes
2 parents 7a6a64c + fc3b1aa commit 367ae24

File tree

11 files changed

+127
-105
lines changed

11 files changed

+127
-105
lines changed

docs/changelog.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
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.
44

5-
## [0.11.6] - 02-08-2024
5+
## [0.11.6] - 02-26-2024
6+
7+
- Fix some bugs introduced as a result of the last release:
8+
- tag's were being removed from the URL params when selecting a project view
9+
- stabilized query params along the way on the namespace page
10+
11+
## [0.11.7] - 02-22-2024
612

713
- Added interface for selecting and viewing project views
814
- optimized loading of very large sample tables

pephub/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.11.7"
1+
__version__ = "0.11.8"

pephub/dependencies.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,38 @@ def get_project(
209209
)
210210

211211

212+
def get_config(
213+
namespace: str,
214+
project: str,
215+
tag: Optional[str] = DEFAULT_TAG,
216+
agent: PEPDatabaseAgent = Depends(get_db),
217+
) -> Dict[str, Any]:
218+
try:
219+
config = agent.project.get_config(namespace, project, tag)
220+
yield config
221+
except ProjectNotFoundError:
222+
raise HTTPException(
223+
404,
224+
f"PEP '{namespace}/{project}:{tag or DEFAULT_TAG}' does not exist in database. Did you spell it correctly?",
225+
)
226+
227+
228+
def get_subsamples(
229+
namespace: str,
230+
project: str,
231+
tag: Optional[str] = DEFAULT_TAG,
232+
agent: PEPDatabaseAgent = Depends(get_db),
233+
) -> Dict[str, Any]:
234+
try:
235+
subsamples = agent.project.get_subsamples(namespace, project, tag)
236+
yield subsamples
237+
except ProjectNotFoundError:
238+
raise HTTPException(
239+
404,
240+
f"PEP '{namespace}/{project}:{tag or DEFAULT_TAG}' does not exist in database. Did you spell it correctly?",
241+
)
242+
243+
212244
def get_project_annotation(
213245
namespace: str,
214246
project: str,

pephub/routers/api/v1/project.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
from ....dependencies import (
4545
get_db,
4646
get_project,
47+
get_config,
48+
get_subsamples,
4749
get_project_annotation,
4850
get_namespace_access_list,
4951
verify_user_can_fork,
@@ -413,29 +415,21 @@ async def get_pep_samples(
413415

414416
@project.get("/config", summary="Get project configuration file")
415417
async def get_pep_config(
416-
proj: Union[peppy.Project, dict] = Depends(get_project),
418+
config: dict = Depends(get_config),
417419
format: Optional[Literal["JSON", "String"]] = "JSON",
418-
raw: Optional[bool] = False,
419420
):
420421
"""
421422
Get project configuration file from a certain project and namespace
422423
423-
Don't have a namespace or project?
424-
425424
Use the following:
426425
427426
project: example
428427
namespace: databio
428+
tag: default
429429
"""
430-
if raw:
431-
proj_config = proj[CONFIG_KEY]
432-
else:
433-
proj_config = proj.to_dict(extended=True, orient="records")[CONFIG_KEY]
434-
if format == "JSON":
435-
return JSONResponse(proj_config)
436430
return JSONResponse(
437431
{
438-
"config": yaml.dump(proj_config, sort_keys=False),
432+
"config": yaml.dump(config, sort_keys=False),
439433
}
440434
)
441435

@@ -622,8 +616,8 @@ async def delete_sample(
622616

623617

624618
@project.get("/subsamples")
625-
async def get_subsamples(
626-
proj: peppy.Project = Depends(get_project),
619+
async def get_subsamples_endpoint(
620+
subsamples: peppy.Project = Depends(get_subsamples),
627621
download: bool = False,
628622
):
629623
"""
@@ -636,16 +630,11 @@ async def get_subsamples(
636630
project: example
637631
namespace: databio
638632
"""
639-
if isinstance(proj, dict):
640-
subsamples = proj[SUBSAMPLE_RAW_LIST_KEY]
641-
else:
642-
subsamples = proj.to_dict(extended=True, orient="records")[
643-
SUBSAMPLE_RAW_LIST_KEY
644-
]
633+
645634
if subsamples:
646635
try:
647636
subsamples = pd.DataFrame(
648-
proj[SUBSAMPLE_RAW_LIST_KEY][0]
637+
subsamples[0]
649638
) # TODO: update this enpoint, so that it has access to all subsample tables
650639
except IndexError:
651640
subsamples = pd.DataFrame()
@@ -861,6 +850,7 @@ async def create_view_of_the_project(
861850
tag: str = DEFAULT_TAG,
862851
description: str = "",
863852
sample_names: List[str] = None,
853+
no_fail: bool = False,
864854
namespace_access_list: List[str] = Depends(get_namespace_access_list),
865855
agent: PEPDatabaseAgent = Depends(get_db),
866856
):
@@ -875,6 +865,7 @@ async def create_view_of_the_project(
875865
try:
876866
agent.view.create(
877867
view_name=view,
868+
no_fail=no_fail,
878869
description=description,
879870
view_dict=CreateViewDictModel(
880871
project_namespace=namespace,

requirements/requirements-all.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
fastapi>=0.108.0
22
psycopg>=3.1.15
3-
pepdbagent @ git+https://github.com/pepkit/pepdbagent.git@dev#egg=pepdbagent
4-
# pepdbagent>=0.7.3
3+
# pepdbagent @ git+https://github.com/pepkit/pepdbagent.git@dev#egg=pepdbagent
4+
pepdbagent>=0.8.0
55
peppy>=0.40.1
66
eido>=0.2.2
77
jinja2>=3.1.2

web/src/components/namespace/search-bar.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { FC } from 'react';
22
import { set } from 'react-hook-form';
3+
import { useSearchParams } from 'react-router-dom';
34

45
interface Props {
56
namespace: string;
@@ -26,6 +27,7 @@ export const NamespacePageSearchBar: FC<Props> = ({
2627
setOrder,
2728
setOffset,
2829
}) => {
30+
const [searchParams, setSearchParams] = useSearchParams();
2931
return (
3032
<div className="flex-row d-flex align-items-center" style={{ position: 'relative' }}>
3133
<div className="input-group shadow-sm">
@@ -37,6 +39,12 @@ export const NamespacePageSearchBar: FC<Props> = ({
3739
onChange={(e) => {
3840
setSearch(e.target.value);
3941
setOffset(0);
42+
if (e.target.value === '') {
43+
searchParams.delete('search');
44+
} else {
45+
searchParams.set('search', e.target.value);
46+
}
47+
setSearchParams(searchParams);
4048
}}
4149
id="search-bar"
4250
type="text"
@@ -47,9 +55,8 @@ export const NamespacePageSearchBar: FC<Props> = ({
4755
value={limit}
4856
onChange={(e) => {
4957
setLimit(parseInt(e.target.value));
50-
const urlParams = new URLSearchParams(window.location.search);
51-
urlParams.set('limit', e.target.value);
52-
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
58+
searchParams.set('limit', e.target.value);
59+
setSearchParams(searchParams);
5360
}}
5461
className="form-control form-select"
5562
>
@@ -64,10 +71,8 @@ export const NamespacePageSearchBar: FC<Props> = ({
6471
const [orderBy, order] = e.target.value.split('+');
6572
setOrderBy(orderBy);
6673
setOrder(order);
67-
const urlParams = new URLSearchParams(window.location.search);
68-
urlParams.set('orderBy', orderBy);
69-
urlParams.set('order', order);
70-
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
74+
searchParams.set('orderBy', orderBy);
75+
setSearchParams(searchParams);
7176
}}
7277
className="form-control form-select"
7378
>

web/src/components/namespace/star-filter-bar.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { FC } from 'react';
2+
import { useSearchParams } from 'react-router-dom';
23

34
interface Props {
45
search: string;
56
setSearch: (search: string) => void;
67
}
78

89
export const StarFilterBar: FC<Props> = ({ search, setSearch }) => {
10+
const [searchParams, setSearchParams] = useSearchParams();
911
return (
1012
<div className="flex-row d-flex align-items-center" style={{ position: 'relative' }}>
1113
<div className="input-group shadow-sm">
@@ -14,7 +16,15 @@ export const StarFilterBar: FC<Props> = ({ search, setSearch }) => {
1416
</span>
1517
<input
1618
value={search}
17-
onChange={(e) => setSearch(e.target.value)}
19+
onChange={(e) => {
20+
setSearch(e.target.value);
21+
if (e.target.value === '') {
22+
searchParams.delete('search');
23+
} else {
24+
searchParams.set('search', e.target.value);
25+
}
26+
setSearchParams(searchParams);
27+
}}
1828
id="search-bar"
1929
type="text"
2030
className="form-control w-60"

web/src/components/namespace/view-selector.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FC } from 'react';
22
import Nav from 'react-bootstrap/Nav';
3-
import { NavLink } from 'react-router-dom';
3+
import { NavLink, useSearchParams } from 'react-router-dom';
44

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

@@ -14,7 +14,13 @@ type Props = {
1414
};
1515

1616
export const NamespaceViewSelector: FC<Props> = (props) => {
17+
const [searchParams, setSearchParams] = useSearchParams();
1718
const handleNavSelect = (eventKey: string | null) => {
19+
if (eventKey === null) {
20+
return;
21+
}
22+
searchParams.set('view', eventKey);
23+
setSearchParams(searchParams);
1824
props.setView(eventKey as View);
1925
};
2026

web/src/components/project/view-selector.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useSearchParams } from 'react-router-dom';
55
import ReactSelect from 'react-select';
66

77
import { ProjectViewsResponse } from '../../api/project';
8+
import { search } from '../../api/search';
89

910
interface Props {
1011
projectViewsIsLoading: boolean;
@@ -39,22 +40,15 @@ export const ViewSelector = (props: Props) => {
3940
})) || []
4041
}
4142
onChange={(selectedOption) => {
43+
debugger;
4244
if (selectedOption === null) {
4345
setView(undefined);
4446
searchParams.delete('view');
45-
setSearchParams(
46-
new URLSearchParams({
47-
...searchParams,
48-
}),
49-
);
47+
setSearchParams(searchParams);
5048
} else {
5149
setView(selectedOption.value);
52-
setSearchParams(
53-
new URLSearchParams({
54-
...searchParams,
55-
view: selectedOption.value,
56-
}),
57-
);
50+
searchParams.set('view', selectedOption.value);
51+
setSearchParams(searchParams);
5852
}
5953
}}
6054
isDisabled={projectViews?.views.length === 0 || projectViewsIsLoading}

web/src/pages/Namespace.tsx

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FC, Fragment, useEffect, useState } from 'react';
22
import Breadcrumb from 'react-bootstrap/Breadcrumb';
3-
import { useParams } from 'react-router-dom';
3+
import { useParams, useSearchParams } from 'react-router-dom';
44

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

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

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

3837
// pagination
39-
const [limit, setLimit] = useState(urlParams.get('limit') ? parseInt(urlParams.get('limit')!) : 10);
40-
const [offset, setOffset] = useState(urlParams.get('offset') ? parseInt(urlParams.get('offset')!) : 0);
41-
const [search, setSearch] = useState(urlParams.get('search') || '');
42-
const [orderBy, setOrderBy] = useState(urlParams.get('orderBy') || 'update_date');
43-
const [order, setOrder] = useState(urlParams.get('order') || 'asc');
38+
const [limit, setLimit] = useState(searchParams.get('limit') ? parseInt(searchParams.get('limit')!) : 10);
39+
const [offset, setOffset] = useState(searchParams.get('offset') ? parseInt(searchParams.get('offset')!) : 0);
40+
const [search, setSearch] = useState(searchParams.get('search') || '');
41+
const [orderBy, setOrderBy] = useState(searchParams.get('orderBy') || 'update_date');
42+
const [order, setOrder] = useState(searchParams.get('order') || 'asc');
4443

4544
// state
4645
const [showAddPEPModal, setShowAddPEPModal] = useState(false);
4746
const [showEndpointsModal, setShowEndpointsModal] = useState(false);
4847
const [showGeoDownloadModal, setShowGeoDownloadModal] = useState(false);
4948
const [view, setView] = useState<View>(viewFromUrl === 'stars' ? 'stars' : 'peps');
50-
const [starSearch, setStarSearch] = useState<string>(urlParams.get('starSearch') || '');
49+
const [starSearch, setStarSearch] = useState<string>(searchParams.get('starSearch') || '');
5150

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

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

78-
// update url when search changes
79-
useEffect(() => {
80-
if (typeof window !== 'undefined' && search === '') {
81-
const urlParams = new URLSearchParams(window.location.search);
82-
urlParams.delete('search');
83-
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
84-
} else if (typeof window !== 'undefined') {
85-
const urlParams = new URLSearchParams(window.location.search);
86-
urlParams.set('search', search);
87-
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
88-
}
89-
}, [search]);
90-
91-
useEffect(() => {
92-
if (typeof window !== 'undefined' && starSearch === '') {
93-
const urlParams = new URLSearchParams(window.location.search);
94-
urlParams.delete('starSearch');
95-
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
96-
} else if (typeof window !== 'undefined') {
97-
const urlParams = new URLSearchParams(window.location.search);
98-
urlParams.set('starSearch', starSearch);
99-
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams}`);
100-
}
101-
}, [starSearch]);
102-
10377
if (namespaceInfoIsLoading || starsIsLoading) {
10478
return (
10579
<PageLayout title={namespace}>

0 commit comments

Comments
 (0)