Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
33bc2e7
track curation_uuid in studyset_study
jdkent Dec 6, 2025
7c19ff1
modify frontend to accomodate exporting curation_uuid
jdkent Dec 6, 2025
76a41db
make sure stub follows user specified changes
jdkent Dec 6, 2025
8f05eb4
update openapi
jdkent Dec 6, 2025
2d25d31
fix tests in neurostore
jdkent Dec 6, 2025
e1dd796
fix revision
jdkent Dec 6, 2025
c668e7f
fix ingestion test
jdkent Dec 6, 2025
66f38b2
expose studyset_studies
jdkent Dec 6, 2025
ef92015
refactor
jdkent Dec 6, 2025
0e77732
fix cypress test
jdkent Dec 6, 2025
e011869
suppress warnings with overlaps
jdkent Dec 6, 2025
01ea6aa
try to fix ingestion test
jdkent Dec 6, 2025
277e332
fix cypress test
jdkent Dec 7, 2025
d39b0c9
use the stub_uuid
jdkent Dec 7, 2025
9383f80
fix more blind spots
jdkent Dec 7, 2025
91f4693
update studyset_study PUT logic
jdkent Dec 7, 2025
e0b7d03
fix the extraction logic
jdkent Dec 7, 2025
d355a6e
maybe fix
jdkent Dec 7, 2025
5c17ebf
add test that emulates the change
jdkent Dec 7, 2025
9bd199c
change the logic of ingestion
jdkent Dec 7, 2025
8869696
undo unnecessary changes
jdkent Dec 7, 2025
612f569
fix style issues
jdkent Dec 7, 2025
8bd8c33
fix data check guards
jdkent Dec 7, 2025
c16539c
add script and fix tests
jdkent Dec 7, 2025
8052574
fix style
jdkent Dec 7, 2025
1949a9e
make base study call more friendly
jdkent Dec 7, 2025
96aa675
fix the frontend client api usage
jdkent Dec 7, 2025
4278624
review of code
jdkent Dec 7, 2025
27f6369
fix style
jdkent Dec 7, 2025
b9cea5a
fix style again
jdkent Dec 7, 2025
9f723e4
remove redundant code
jdkent Dec 7, 2025
c60150a
Apply suggestions from code review
jdkent Dec 7, 2025
0b86b2c
fix line length issue
jdkent Dec 7, 2025
33ff8c4
do not use overlaps, be explicit about purpose of columns
jdkent Dec 7, 2025
d903f00
Update store/backend/migrations/versions/8e3f3d8a9b5b_add_curation_st…
jdkent Dec 7, 2025
e284036
realign the basestudies endpoint
jdkent Dec 7, 2025
82dfb37
fix linting errors
jdkent Dec 9, 2025
34c8d7d
fix: minor code improvements, remove unused code
nicoalee Dec 11, 2025
7de1401
remove curation_stub_map from public api
jdkent Dec 11, 2025
7841dc7
remove unused index
jdkent Dec 11, 2025
a4a063c
fix: remove unused imports
nicoalee Dec 12, 2025
d17e769
fix backfill script
jdkent Dec 13, 2025
89cf2fe
assume postgres port
jdkent Dec 13, 2025
6423e8e
arguments were not aligned to create a project
jdkent Dec 13, 2025
055a464
fix backfill script
jdkent Dec 13, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('Ingestion', () => {
fixture: 'IngestionFixtures/annotationsPutFixture',
}).as('annotationFixture');

cy.intercept('POST', `**/api/base-studies/`, {
cy.intercept('POST', '**/api/base-studies*', {
fixture: 'IngestionFixtures/baseStudiesFixture',
}).as('baseStudiesFixture');
});
Expand Down
43 changes: 43 additions & 0 deletions compose/neurosynth-frontend/src/helpers/Extraction.helpers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, expect, it } from 'vitest';
import { mapStubsToStudysetPayload } from './Extraction.helpers';
import { BaseStudyReturn, StudyReturn } from 'neurostore-typescript-sdk';

const makeBaseStudy = (id: string, versionIds: string[]): BaseStudyReturn => ({
id,
versions: versionIds.map<StudyReturn>((vid, idx) => ({
id: vid,
updated_at: idx.toString(), // for sorting fallback
})),
});

describe('mapStubsToStudysetPayload', () => {
it('zips stubs to base studies by index and carries stub UUIDs', () => {
const stubs = [{ id: 'stub-1' }, { id: 'stub-2' }];
const baseStudies: Array<BaseStudyReturn> = [makeBaseStudy('bs1', ['v1a']), makeBaseStudy('bs2', ['v2a'])];

const payload = mapStubsToStudysetPayload(stubs, baseStudies);
expect(payload).toEqual([
{ id: 'v1a', curation_stub_uuid: 'stub-1' },
{ id: 'v2a', curation_stub_uuid: 'stub-2' },
]);
});

it('prefers an existing study ID when present', () => {
const stubs = [{ id: 'stub-1' }];
const existing = new Set<string>(['existing-id']);
const baseStudies: Array<BaseStudyReturn> = [makeBaseStudy('bs1', ['existing-id', 'new-id'])];

const payload = mapStubsToStudysetPayload(stubs, baseStudies, existing);
expect(payload[0]).toEqual({ id: 'existing-id', curation_stub_uuid: 'stub-1' });
});

it('returns the locked existing mapping even if ingest lacks that version', () => {
const stubs = [{ id: 'stub-1' }];
// ingest returned only a different version
const baseStudies: Array<BaseStudyReturn> = [makeBaseStudy('bs1', ['different-id'])];
const lockedMap = new Map<string, string>([['stub-1', 'existing-id']]);

const payload = mapStubsToStudysetPayload(stubs, baseStudies, undefined, lockedMap);
expect(payload[0]).toEqual({ id: 'existing-id', curation_stub_uuid: 'stub-1' });
});
});
48 changes: 44 additions & 4 deletions compose/neurosynth-frontend/src/helpers/Extraction.helpers.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { StudyReturn, BaseStudy } from 'neurostore-typescript-sdk';
import { StudyReturn, BaseStudy, BaseStudyReturn } from 'neurostore-typescript-sdk';
import { lastUpdatedAtSortFn } from 'helpers/utils';
import { ICurationStubStudy } from 'pages/Curation/Curation.types';

export const selectBestBaseStudyVersion = (baseStudyVersions: Array<StudyReturn>) => {
const sortedVersion = [...baseStudyVersions.sort(lastUpdatedAtSortFn)];
Expand All @@ -8,11 +9,50 @@ export const selectBestBaseStudyVersion = (baseStudyVersions: Array<StudyReturn>

export const selectBestVersionsForStudyset = (baseStudies: Array<BaseStudy>): string[] => {
const selectedVersions = baseStudies.map((baseStudy) => {
const studyVersion = selectBestBaseStudyVersion(
(baseStudy?.versions || []) as StudyReturn[]
);
const studyVersion = selectBestBaseStudyVersion((baseStudy?.versions || []) as StudyReturn[]);
return studyVersion.id as string;
});

return selectedVersions;
};

type StubLike = Pick<ICurationStubStudy, 'id'>;

export const mapStubsToStudysetPayload = (
stubs: Array<StubLike>,
stubBaseStudies: Array<BaseStudyReturn>,
existingStudyIds?: Set<string>,
lockedStubToStudyId?: Map<string, string>
): Array<{ id: string; curation_stub_uuid: string }> => {
const payload: Array<{ id: string; curation_stub_uuid: string }> = [];

stubs.forEach((stub, idx) => {
const lockedStudyId = lockedStubToStudyId?.get(stub.id);
if (lockedStudyId) {
payload.push({
id: lockedStudyId,
curation_stub_uuid: stub.id,
});
return;
}

const stubBaseStudy = stubBaseStudies[idx];
if (!stubBaseStudy) return;

const versions = Array.isArray(stubBaseStudy.versions) ? (stubBaseStudy.versions as Array<StudyReturn>) : [];

// Prefer a version that already exists in the studyset
const foundVersion = versions.find((studyVersion) => existingStudyIds?.has(studyVersion.id || ''));

const chosenVersion = foundVersion || selectBestBaseStudyVersion(versions);

if (chosenVersion?.id) {
payload.push({
id: chosenVersion.id,
curation_stub_uuid: stub.id,
});
}
});

return payload;
};
14 changes: 0 additions & 14 deletions compose/neurosynth-frontend/src/hooks/__mocks__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,6 @@ const useCreateMetaAnalysis = vi.fn().mockReturnValue({
createMetaAnalysis: vi.fn().mockReturnValue(Promise.resolve()),
});

const useGetStudysets = vi.fn().mockReturnValue({
error: undefined,
isLoading: false,
isError: false,
data: {
metadata: {
total_count: 100,
unique_count: 100,
},
results: mockStudysets(),
},
});

const useCreateStudyset = vi.fn().mockReturnValue({
// isLoading: false,
// isError: false,
Expand Down Expand Up @@ -211,7 +198,6 @@ export {
useGetFullText,
useGetStudyById,
useGetStudysetById,
useGetStudysets,
useInputValidation,
useIsMounted,
useUpdateAnalysis,
Expand Down
2 changes: 0 additions & 2 deletions compose/neurosynth-frontend/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import useGetStudysetById from './studysets/useGetStudysetById';
import useCreateAnnotation from './analyses/useCreateAnnotation';
import useCreateStudy from './studies/useCreateStudy';
import useCreateProject from './projects/useCreateProject';
import useGetStudysets from './studysets/useGetStudysets';
import useDeleteStudyset from './studysets/useDeleteStudyset';
import useGetBaseStudies from './studies/useGetBaseStudies';
import useGetStudyById from './studies/useGetStudyById';
Expand Down Expand Up @@ -69,7 +68,6 @@ export {
// META-ANALYSIS RESULTS
useGetMetaAnalysisResultById,
// STUDYSETS
useGetStudysets,
useGetStudysetById,
useCreateStudyset,
useDeleteStudyset,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ const useCreateProject = () => {
const queryClient = useQueryClient();
const { enqueueSnackbar } = useSnackbar();
return useMutation<AxiosResponse<ProjectReturn>, AxiosError, Project, unknown>(
(project) => API.NeurosynthServices.ProjectsService.projectsPost(project),
(project) =>
// Signature: projectsPost(sourceId?, copyAnnotations?, project)
// Explicitly pass `undefined` for the optional query params so the payload is treated as the body argument.
API.NeurosynthServices.ProjectsService.projectsPost(
undefined, // sourceId
undefined, // copyAnnotations
project
),
{
onSuccess: () => {
// update queries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ import API from 'utils/api';

export const baseStudiesSearchHelper = (searchCriteria: Partial<SearchCriteria>) => {
return API.NeurostoreServices.StudiesService.baseStudiesGet(
searchCriteria.isNested,
undefined, // year_min
undefined, // x
undefined, // y
undefined, // z
undefined, // radius
undefined, // year_max
undefined, // feature_filter
undefined, // pipeline_config
undefined, // feature_display
undefined, // semantic_search
undefined, // pipeline_config_id
undefined, // distance_threshold
undefined, // overall_cap
undefined, // feature_flatten
searchCriteria.genericSearchStr || undefined,
searchCriteria.sortBy === SortBy.RELEVANCE ? undefined : searchCriteria.sortBy,
searchCriteria.pageOfResults,
Expand All @@ -14,6 +29,7 @@ export const baseStudiesSearchHelper = (searchCriteria: Partial<SearchCriteria>)
searchCriteria.authorSearch || undefined,
'group',
searchCriteria.dataType === SearchDataType.ALL ? 'both' : searchCriteria.dataType,
undefined, // is_oa
searchCriteria.journalSearch || undefined,
searchCriteria.pmid,
searchCriteria.doi,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import API from 'utils/api';
const useCreateStudyset = () => {
const queryClient = useQueryClient();
return useMutation<AxiosResponse<StudysetReturn>, AxiosError, StudysetRequest, unknown>(
(studyset) => API.NeurostoreServices.StudySetsService.studysetsPost(studyset),
(studyset) => API.NeurostoreServices.StudySetsService.studysetsPost(undefined, undefined, undefined, studyset),
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three undefined parameters suggest the API signature may have changed. Consider using named parameters or an options object to make the call site more maintainable and less error-prone.

Suggested change
(studyset) => API.NeurostoreServices.StudySetsService.studysetsPost(undefined, undefined, undefined, studyset),
(studyset) => API.NeurostoreServices.StudySetsService.studysetsPost({ studyset }),

Copilot uses AI. Check for mistakes.
{
onSuccess: () => {
// update study
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { StudyReturn, StudysetReturn } from 'neurostore-typescript-sdk';
import { useSnackbar } from 'notistack';
import { useQuery } from 'react-query';
import API from 'utils/api';
import { STUDYSET_QUERY_STRING } from './useGetStudysets';

export const STUDYSET_QUERY_STRING = 'studysets';

const useGetStudysetById = (studysetId?: string, nested?: boolean) => {
const { enqueueSnackbar } = useSnackbar();
Expand All @@ -17,7 +18,7 @@ const useGetStudysetById = (studysetId?: string, nested?: boolean) => {
() => API.NeurostoreServices.StudySetsService.studysetsIdGet(studysetId || '', nested),
{
enabled: !!studysetId,
onError: (err) => {
onError: () => {
enqueueSnackbar('there was an error retrieving the studyset', { variant: 'error' });
},
select: (res) => {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,14 @@ const useUpdateStudyset = () => {
studyset: StudysetRequest;
},
unknown
>(
(args) =>
API.NeurostoreServices.StudySetsService.studysetsIdPut(args.studysetId, args.studyset),
{
onSuccess: () => {
queryClient.invalidateQueries('studysets');
},
onError: () => {
enqueueSnackbar('there was an error updating the studyset', { variant: 'error' });
},
}
);
>((args) => API.NeurostoreServices.StudySetsService.studysetsIdPut(args.studysetId, args.studyset), {
onSuccess: () => {
queryClient.invalidateQueries('studysets');
},
onError: () => {
enqueueSnackbar('there was an error updating the studyset', { variant: 'error' });
},
});
};

export default useUpdateStudyset;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const ExtractionOutOfSyncStyles: Style = {
marginBottom: '1rem',
position: 'sticky',
top: '1.5rem',
marginTop: '1rem',
zIndex: 10,
},
};
Expand Down
Loading
Loading