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

FD-1892 - Using Locutus Search for Mappings #167

Merged
merged 11 commits into from
Jan 8, 2025
8 changes: 4 additions & 4 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { GoogleOAuthProvider } from '@react-oauth/google';
export const myContext = createContext();

function App() {
const searchUrl = import.meta.env.VITE_SEARCH_ENDPOINT;
const monarchUrl = import.meta.env.VITE_MONARCH_SEARCH;
const vocabUrl = import.meta.env.VITE_VOCAB_ENDPOINT;
const clientId = import.meta.env.VITE_CLIENT_ID;
const mapDragonVersion = import.meta.env.VITE_MAPDRAGON_VERSION;
Expand Down Expand Up @@ -42,14 +40,15 @@ function App() {
message.config({
top: '25vh',
});

const defaultOntologies = 'MONDO,HP,MAXO,NCIT';

return (
<GoogleOAuthProvider clientId={clientId}>
<myContext.Provider
value={{
results,
setResults,
searchUrl,
monarchUrl,
vocabUrl,
mapDragonVersion,
tablesDD,
Expand Down Expand Up @@ -82,6 +81,7 @@ function App() {
setOntologyForPagination,
ucumCodes,
setUcumCodes,
defaultOntologies,
}}
>
<AppRouter />
Expand Down
34 changes: 17 additions & 17 deletions src/AppRouter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,24 +60,24 @@ export const AppRouter = () => {
>
<Route index element={<OntologySearch />} />
<Route element={<PageLayout />}>
<Route path="/search/:query" element={<SearchResults />} />
<Route path="/404" element={<Error404 />} />
<Route element={<SearchContextRoot />}>
<Route path="/search/:query" element={<SearchResults />} />
<Route path="/404" element={<Error404 />} />

<Route path="/ontologies" element={<OntologyInfo />} />
<Route path="/about" element={<About />} />
<Route
path="/terminologies"
element={
isLoggedIn() ? <TerminologyList /> : <Navigate to="/login" />
}
/>
<Route path="/login" element={<LoginPage />} />
<Route
path="/terminology"
element={<Navigate to="/terminologies" />}
/>
<Route element={<MappingContextRoot />}>
<Route element={<SearchContextRoot />}>
<Route path="/ontologies" element={<OntologyInfo />} />
<Route path="/about" element={<About />} />
<Route
path="/terminologies"
element={
isLoggedIn() ? <TerminologyList /> : <Navigate to="/login" />
}
/>
<Route path="/login" element={<LoginPage />} />
<Route
path="/terminology"
element={<Navigate to="/terminologies" />}
/>
<Route element={<MappingContextRoot />}>
<Route
path="/studies"
element={
Expand Down
14 changes: 13 additions & 1 deletion src/Contexts/SearchContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ export function SearchContextRoot() {
const [ontologyApis, setOntologyApis] = useState([]);
const [apiPreferencesTerm, setApiPreferencesTerm] = useState(undefined);
const [searchText, setSearchText] = useState('');
const [checkedOntologies, setCheckedOntologies] = useState([]);
const [moreAvailable, setMoreAvailable] = useState(false);
const [resultsCount, setResultsCount] = useState();

const defaultOntologies = 'mondo,hp,maxo,ncit';
const entriesPerPage = 100;

const defaultOntologies = 'MONDO,HP,MAXO,NCIT';
const preferenceTypeSet = data =>
apiPreferencesTerm ? setApiPreferencesTerm(data) : setApiPreferences(data);

Expand Down Expand Up @@ -66,6 +71,13 @@ export function SearchContextRoot() {
prefTypeKey,
searchText,
setSearchText,
checkedOntologies,
setCheckedOntologies,
entriesPerPage,
moreAvailable,
setMoreAvailable,
resultsCount,
setResultsCount,
};

return (
Expand Down
86 changes: 58 additions & 28 deletions src/components/Manager/FetchManager.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ontologyReducer } from './Utilitiy';
import { ontologyReducer } from './Utility';

// Fetches all elements at an endpoint
export const getAll = (vocabUrl, name, navigate) => {
Expand Down Expand Up @@ -169,24 +169,23 @@ export const getOntologies = vocabUrl => {
};

export const olsFilterOntologiesSearch = (
searchUrl,
vocabUrl,
query,
ontologiesToSearch,
page,
entriesPerPage,
pageStart,
selectedBoxes,
setTotalCount,
setResults,
setFilteredResultsCount,
setResultsCount,
setLoading,
results,
setFacetCounts
setMoreAvailable
) => {
setLoading(true);

return fetch(
`${searchUrl}q=${query}&ontology=${ontologiesToSearch}&rows=${entriesPerPage}&start=${pageStart}`,
`${vocabUrl}/ontology_search?keyword=${query}&selected_ontologies=${ontologiesToSearch}&selected_api=ols&results_per_page=${entriesPerPage}&start_index=${pageStart}`,
{
method: 'GET',
headers: {
Expand All @@ -196,35 +195,22 @@ export const olsFilterOntologiesSearch = (
)
.then(res => res.json())
.then(data => {
// filters results through the ontologyReducer function (defined in Manager/Utility.jsx)

let res = ontologyReducer(data?.response?.docs);
// if the page > 0 (i.e. if this is not the first batch of results), the new results
// are concatenated to the old
if (selectedBoxes) {
res.results = res.results.filter(
d => !selectedBoxes.some(box => box.obo_id === d.obo_id)
data.results = data.results.filter(
d => !selectedBoxes.some(box => box.code === d.code)
);
}

if (page > 0 && results.length > 0) {
res.results = results.concat(res.results);

// Apply filtering to remove results with obo_id in selectedBoxes
} else {
// Set the total number of search results for pagination
setTotalCount(data.response.numFound);
if (page > 0 && results?.length > 0) {
data.results = results?.concat(data.results);
}

//the results are set to res (the filtered, concatenated results)
setResults(data.results);
setMoreAvailable(data.more_results_available);

setResults(res.results);
setFilteredResultsCount(
prevState => prevState + res?.filteredResults?.length
);
// resultsCount is set to the length of the filtered, concatenated results for pagination
setResultsCount(res.results.length);
setFacetCounts(data?.facet_counts?.facet_fields?.ontologyPreferredPrefix);
setResultsCount(data?.results?.length);
})
.finally(() => setLoading(false));
};
Expand Down Expand Up @@ -271,13 +257,57 @@ export const getFiltersByCode = (

if (Array.isArray(ols)) {
// If ols in api_preference is an array, use it as is
setApiPreferencesCode(ols); // Set state to the array
setApiPreferencesCode(ols.map(item => item.toUpperCase())); // Set state to the array
} else if (typeof ols === 'string') {
// If ols in api_preference is a string, split it into an array
const splitOntologies = ols.split(',');
const splitOntologies = ols.toUpperCase().split(',');
setApiPreferencesCode(splitOntologies); // Set state to the array
} else {
setApiPreferencesCode([]); // Fallback if no ols found
}
});
};

export const ontologyFilterCodeSubmit = (
apiPreferencesCode,
preferenceType,
prefTypeKey,
mappingProp,
vocabUrl,
table,
terminology
) => {
const apiPreference = { api_preference: { 'ols': [] } };
if (
apiPreferencesCode &&
(!preferenceType[prefTypeKey]?.api_preference?.[0] ||
JSON.stringify(
Object.values(preferenceType[prefTypeKey]?.api_preference)[0]?.sort()
) !== JSON.stringify(apiPreferencesCode?.sort()))
) {
apiPreference.api_preference.ols = apiPreferencesCode;
const fetchUrl = `${vocabUrl}/${
!table ? `Terminology/${terminology?.id}` : `Table/${table?.id}`
}/filter/${mappingProp}`;
fetch(fetchUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(apiPreference),
})
.then(res => {
if (res.ok) {
return res.json();
} else {
throw new Error('An unknown error occurred.');
}
})
.catch(error => {
if (error) {
notification.error({
message: 'Error',
description: 'An error occurred saving the ontology preferences.',
});
}
});
}
};
20 changes: 18 additions & 2 deletions src/components/Manager/MappingsFunctions/AssignMappings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AssignMappingsCheckboxes } from './AssignMappingsCheckboxes';
import { ModalSpinner } from '../Spinner';
import { MappingContext } from '../../../Contexts/MappingContext';
import { SearchContext } from '../../../Contexts/SearchContext';
import { ontologyFilterCodeSubmit } from '../FetchManager';

export const AssignMappings = ({
setSelectedKey,
Expand All @@ -16,7 +17,13 @@ export const AssignMappings = ({
const [form] = Form.useForm();

const { vocabUrl, user } = useContext(myContext);
const { prefTerminologies, setApiResults } = useContext(SearchContext);
const {
prefTerminologies,
setApiResults,
preferenceType,
prefTypeKey,
apiPreferencesCode,
} = useContext(SearchContext);
const { setMapping, idsForSelect, setIdsForSelect } =
useContext(MappingContext);
const [terminologiesToMap, setTerminologiesToMap] = useState([]);
Expand Down Expand Up @@ -66,7 +73,7 @@ export const AssignMappings = ({
? item.description[0]
: item.description,
system: item.system,
mapping_relationship: idsForSelect[item.obo_id || item.code],
mapping_relationship: idsForSelect[item.code],
}));
const mappingsDTO = {
mappings: selectedMappings,
Expand Down Expand Up @@ -97,6 +104,15 @@ export const AssignMappings = ({
message.success('Changes saved successfully.');
})
.finally(() => setLoading(false));
ontologyFilterCodeSubmit(
apiPreferencesCode,
preferenceType,
prefTypeKey,
mappingProp,
vocabUrl,
null,
terminology
);
};

return (
Expand Down
Loading
Loading