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

Feature/patient info #110

Merged
merged 32 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
86f5277
Ingest menu button
justin-ys Aug 8, 2023
7913748
Basic ingest page tab navigation
justin-ys Aug 10, 2023
ba9da8f
Update package-lock.json
justin-ys Aug 14, 2023
f9bb73c
Ingest page code style fixes
justin-ys Aug 14, 2023
b1ea774
Remove permissions tab
justin-ys Aug 14, 2023
b510895
Add navigation button
justin-ys Aug 15, 2023
7b9046b
Basic clinical ingest page skeleton
justin-ys Aug 16, 2023
3fdf7af
Basic genomic ingest page skeleton
justin-ys Aug 17, 2023
23ff72b
File upload only accept JSON
justin-ys Aug 17, 2023
4b075b6
Persistent file dialogs
justin-ys Aug 17, 2023
2ba2b96
Validate JSON upload
justin-ys Aug 18, 2023
77a012f
[wip]
OrdiNeu Sep 6, 2023
7d3533f
Rework clinical&genomic data to work with the new query microservice
OrdiNeu Sep 13, 2023
1ffc37c
Sidebar merging, table, topLevel
CourtneyGosselin Nov 15, 2023
760cc9a
Patient info
CourtneyGosselin Dec 22, 2023
d75a2db
Fix parent ID addition in the table
CourtneyGosselin Dec 22, 2023
91e2b48
merge develop inot patient-info
CourtneyGosselin Jan 2, 2024
f68347c
Katsu UUID changes, minor styling adjustment
CourtneyGosselin Jan 4, 2024
6454b6e
ESlint fixes, pullout functions, PR review changes
CourtneyGosselin Jan 12, 2024
ca1abcf
Eslint and empty object fix
CourtneyGosselin Jan 12, 2024
73e7b5f
Merge in develop fix conflicts
CourtneyGosselin Jan 12, 2024
814c9f6
Fix up the datarow componnent
OrdiNeu Jan 12, 2024
5c90f9a
Quick fix to a warning that's been bugging me for forever
OrdiNeu Jan 12, 2024
85e014a
Merge branch 'fnguyen/version-fixup' of github.com:CanDIG/candig-data…
CourtneyGosselin Jan 12, 2024
34d347b
styling and develop fixing
CourtneyGosselin Jan 17, 2024
7ac89c5
Selection highlight in sidebar
CourtneyGosselin Jan 17, 2024
e846890
Sidebar height matching
CourtneyGosselin Jan 17, 2024
f05bdae
Update package-lock.json
CourtneyGosselin Jan 17, 2024
986678e
Prettier linter changes
CourtneyGosselin Jan 17, 2024
a3b2389
ESlint fixes
CourtneyGosselin Jan 17, 2024
b464d2a
Top level json fix
CourtneyGosselin Jan 17, 2024
8374f3b
prettier fix
CourtneyGosselin Jan 17, 2024
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
3 changes: 2 additions & 1 deletion .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ REACT_APP_KATSU_API_SERVER='candig.docker.internal:5080/katsu/v2/'
REACT_APP_FEDERATION_API_SERVER='candig.docker.internal:5080'
REACT_APP_BASE_NAME=''
REACT_APP_SITE_LOCATION = ''
REACT_APP_HTSGET_SERVER = 'candig.docker.internal:5080/genomics/ga4gh/drs/v1/'
REACT_APP_HTSGET_SERVER = ''
REACT_APP_INGEST_SERVER = ''
GENERATE_SOURCEMAP=false
25,567 changes: 16,222 additions & 9,345 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
"react-perfect-scrollbar": "^1.5.8",
"react-promise-tracker": "^2.1.0",
"react-redux": "^7.2.3",
"react-router": "^6.16.0",
"react-router-dom": "^6.3.0",
"react-router": "^6.21.1",
"react-router-dom": "^6.21.1",
"redux": "^4.0.5",
"yup": "^0.32.9"
},
Expand Down
6 changes: 3 additions & 3 deletions src/layout/MainLayout/Sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const useStyles = makeStyles((theme) => ({

// ===========================|| SIDEBAR DRAWER ||=========================== //

const Sidebar = ({ drawerOpen, drawerToggle, window }) => {
const Sidebar = ({ drawerOpen, drawerToggle, screen }) => {
const classes = useStyles();
const theme = useTheme();
const matchUpMd = useMediaQuery(theme.breakpoints.up('md'));
Expand All @@ -70,7 +70,7 @@ const Sidebar = ({ drawerOpen, drawerToggle, window }) => {
</>
);

const container = window !== undefined ? () => window().document.body : undefined;
const container = screen !== undefined ? () => window().document.body : undefined;

return (
<nav className={classes.drawer} aria-label="mailbox folders">
Expand All @@ -95,7 +95,7 @@ const Sidebar = ({ drawerOpen, drawerToggle, window }) => {
Sidebar.propTypes = {
drawerOpen: PropTypes.bool,
drawerToggle: PropTypes.func,
window: PropTypes.object
screen: PropTypes.object
};

export default Sidebar;
4 changes: 2 additions & 2 deletions src/menu-items/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import clinicalGenomicSearch from './clinicalGenomicSearch';
import summary from './summary';
import ingest from './ingest';
// import ingest from './ingest';

// import pages from './pages';
// import utilities from './utilities';
Expand All @@ -9,7 +9,7 @@ import ingest from './ingest';
// ===========================|| MENU ITEMS ||=========================== //

const menuItems = {
items: [summary, clinicalGenomicSearch, ingest /* pages, utilities, other */]
items: [summary, clinicalGenomicSearch /* , ingest, pages, utilities, other */]
};

export default menuItems;
4 changes: 2 additions & 2 deletions src/menu-items/ingest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ const icons = {
// ===========================|| Ingest MENU ITEMS ||=========================== //

const ingest = {
id: 'frontendIngest',
id: 'ingest',
title: 'Data Ingest',
type: 'group',
children: [
{
id: 'Data Ingest',
title: 'Data Ingest',
type: 'item',
url: `${basename}/frontendIngest`,
url: `${basename}/data-ingest`,
icon: icons.IconUpload,
breadcrumbs: false
}
Expand Down
8 changes: 8 additions & 0 deletions src/routes/MainRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ const Summary = Loadable(lazy(() => import('views/summary/summary')));

// Clinical & Genomic Search
const ClinicalGenomicSearch = Loadable(lazy(() => import('views/clinicalGenomic/clinicalGenomicSearch')));
const ClinicalPatientView = Loadable(lazy(() => import('views/clinicalGenomic/clinicalPatientView.js')));

// Ingest Portal
const IngestPortal = Loadable(lazy(() => import('views/ingest/ingest')));

// Ingest Portal
// const IngestPortal = Loadable(lazy(() => import('views/ingest/ingest')));

// Error Pages
const ErrorNotFoundPage = Loadable(lazy(() => import('views/errorPages/ErrorNotFoundPage')));

Expand Down Expand Up @@ -48,6 +52,10 @@ const MainRoutes = {
path: `${basename}/data-ingest`,
element: <IngestPortal />
}, */
{
path: `${basename}/patientView`,
element: <ClinicalPatientView />
},
{
path: `${basename}/frontendIngest`,
element: <IngestPortal />
Expand Down
56 changes: 56 additions & 0 deletions src/store/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,32 @@ export function fetchKatsu(URL) {
});
}

/*
Fetch htsget calls
*/
export function fetchHtsget() {
return fetch(`${federation}/fanout`, {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
method: 'GET',
path: `ga4gh/drs/v1/objects`,
payload: {},
service: 'htsget'
})
})
.then((response) => {
if (response.ok) {
return response.json();
}
return [];
})
.catch((error) => {
console.log(`Error: ${error}`);
return 'error';
});
}

export function fetchFederationStat(endpoint) {
return fetch(`${federation}/fanout`, {
method: 'post',
Expand Down Expand Up @@ -223,3 +249,33 @@ export function query(parameters, abort) {
return 'error';
});
}

/*
Post a clinical data JSON to Katsu
* @param {string}... Name of a gene
*/
export function ingestClinicalData(data) {
return fetch(`${INGEST_URL}/ingest/clinical_donors`, {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: data
})
.then((response) => response.json())
.catch((error) => {
console.log('Error:', error);
return error;
});
}

export function ingestGenomicData(data, program_id) {
return fetch(`${INGEST_URL}/ingest/moh_variants/${program_id}`, {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: data
})
.then((response) => response)
.catch((error) => {
console.log('Error:', error);
return error;
});
}
25 changes: 11 additions & 14 deletions src/ui-component/PersistentFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,28 @@ import { useEffect, useRef, useState } from 'react';
import { Alert, TextField } from '@mui/material';
import PropTypes from 'prop-types';

const PersistentFile = ({ file, setFile, validate = true }) => {
const PersistentFile = ({ file, fileLoader }) => {
// A JSON file input dialog which can maintain its state after unmounting. Optionally validates before upload.
// Calls "fileLoader" with the file object as argument #1 and the JSON data as argument #2.
const [error, setError] = useState(null);
const fileRef = useRef(null);

async function loadFile(file) {
try {
if (validate) {
const result = await file.text().then((data) => JSON.parse(data) && data !== null && data !== undefined);
if (!result) {
console.log(`Error parsing uploaded JSON file ${file.name}`);
setError(`Error parsing uploaded JSON "${file.name}`);
return;
}
const data = await file.text().then((data) => JSON.parse(data));
if (!(data || data !== undefined)) {
console.log(`Error parsing uploaded JSON file ${file.name}`);
setError(`Error parsing uploaded JSON "${file.name}`);
return;
}
fileLoader(file, data);
setError(null);
} catch (error) {
console.log(`Error parsing JSON ${file.name}`);
console.log(error);
setError(`Error parsing JSON "${file.name}": ${error.message}`);
fileRef.current.value = '';
return;
}
setFile(file);
setError(null);
}

useEffect(() => {
Expand Down Expand Up @@ -55,9 +53,8 @@ const PersistentFile = ({ file, setFile, validate = true }) => {
};

PersistentFile.propTypes = {
setFile: PropTypes.func.isRequired,
file: PropTypes.instanceOf(File),
validate: PropTypes.bool
fileLoader: PropTypes.func.isRequired,
file: PropTypes.instanceOf(File)
};

export default PersistentFile;
108 changes: 85 additions & 23 deletions src/ui-component/ingest/ClinicalIngest.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,36 @@ import { Button, Grid, Typography } from '@mui/material';
import PropTypes from 'prop-types';
import { makeField, DataRow } from 'ui-component/DataRow';
import { makeStyles } from '@mui/styles';
import { useEffect, useState } from 'react';
import { fetchFederation } from 'store/api';

const ClinicalIngest = ({ setTab, fileUpload }) => {
const ClinicalIngest = ({ setTab, fileUpload, clinicalData }) => {
// setTab should be a function that sets the tab to the genomic ingest page
const dataRowFields = [
[makeField('Cohort', 'MOCK COHORT'), makeField('Clinical Patients', '850'), makeField('Read Access', '3')],
[makeField('Cohort', 'MOCK COHORT 2'), makeField('Clinical Patients', '325'), makeField('Read Access', '1')],
[makeField('Cohort', 'MOCK COHORT 2'), makeField('Clinical Patients', '78'), makeField('Read Access', '2')]
];

const [authorizedCohorts, setAuthorizedCohorts] = useState([]);

useEffect(() => {
function fetchPrograms() {
return fetchFederation('v2/discovery/donors/', 'katsu')
.then((result) => {
result.forEach((site) => {
const programs = site.results.discovery_donor;
const fields = [];
Object.keys(programs).forEach((program) => {
const field = [
makeField('Cohort', program),
makeField('Clinical Patients', programs[program].toString()),
makeField('Read Access', 'Unknown')
];
fields.push(field);
});
setAuthorizedCohorts(fields);
});
})
.catch((error) => console.log(error));
}
fetchPrograms();
}, []);

const useStyles = makeStyles({
titleText: {
Expand All @@ -21,26 +43,47 @@ const ClinicalIngest = ({ setTab, fileUpload }) => {
color: 'black',
fontSize: '1em',
fontFamily: 'Catamaran'
},
buttonEnabled: {
position: 'absolute',
right: '0.2em',
bottom: '0.2em'
},
buttonDisabled: {
position: 'absolute',
right: '0.2em',
bottom: '0.2em',
backgroundColor: 'grey',
'&:hover': {
backgroundColor: 'grey'
}
}
});

const classes = useStyles();

return (
<>
<Grid container direction="column" spacing={4}>
<Grid item>
<Typography align="left" className={classes.titleText}>
<b>Active cohorts</b>
<b>Your authorized cohorts</b>
</Typography>
<Grid direction="row" spacing={3} container>
{dataRowFields.map((fields, index) => (
<Grid item xs={5} key={index}>
<DataRow rowWidth="100%" itemSize="0.9em" fields={fields} />
</Grid>
))}
</Grid>
{authorizedCohorts.length > 0 ? (
<Grid direction="row" spacing={3} container>
{authorizedCohorts.map((fields, index) => (
<Grid item xs={5} key={index}>
<DataRow rowWidth="100%" itemSize="0.9em" fields={fields} />
</Grid>
))}
</Grid>
) : (
<Typography align="left" className={classes.bodyText}>
No cohorts found.
</Typography>
)}
</Grid>
<Grid item xs={2} md={2} sm={2}>
<Grid item sx={{ width: '100%' }}>
<div>
<Typography align="left" className={classes.titleText}>
<b>Choose a cohort for validation</b>
Expand All @@ -61,29 +104,48 @@ const ClinicalIngest = ({ setTab, fileUpload }) => {
<Typography align="left" className={classes.titleText}>
<b>Live Preview Summary</b>
</Typography>
<Typography sx={{ color: 'grey' }} align="left" className={classes.bodyText}>
Waiting for upload...
</Typography>
{clinicalData === undefined ? (
<Typography sx={{ color: 'grey' }} align="left" className={classes.bodyText}>
Waiting for upload...
</Typography>
) : (
<DataRow
rowWidth="100%"
itemSize="0.9em"
fields={[
makeField('Cohort', clinicalData.donors[0].program_id),
makeField('Clinical Patients', clinicalData.donors.length),
makeField('Read Access', '1')
]}
/>
)}
</Grid>
<Grid item>
<Typography align="left" className={classes.titleText}>
<b>Validation</b>
</Typography>
<Typography sx={{ color: 'grey' }} align="left" className={classes.bodyText}>
Waiting for upload...
Waiting for validation...
</Typography>
</Grid>
</Grid>
<Button sx={{ position: 'absolute', right: '0.2em', bottom: '0.2em' }} onClick={setTab} variant="contained">
Next
</Button>
{clinicalData === undefined ? (
<Button className={classes.buttonDisabled} variant="contained" disabled>
Next
</Button>
) : (
<Button className={classes.buttonEnabled} onClick={setTab} variant="contained">
Next
</Button>
)}
</>
);
};

ClinicalIngest.propTypes = {
setTab: PropTypes.func.isRequired,
fileUpload: PropTypes.element
fileUpload: PropTypes.element,
clinicalData: PropTypes.object
};

export default ClinicalIngest;
Loading
Loading