-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add the EdgeInfoPanel for each knowledge.
- Loading branch information
Showing
30 changed files
with
625 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import React, { useEffect } from 'react'; | ||
import { Empty, Tabs } from 'antd'; | ||
import type { EdgeInfo } from './index.t'; | ||
|
||
import './index.less'; | ||
|
||
type CommonPanelProps = { | ||
edgeInfo?: EdgeInfo; | ||
children?: React.ReactNode; | ||
}; | ||
|
||
const CommonPanel: React.FC<CommonPanelProps> = (props) => { | ||
const { edge, startNode, endNode } = props.edgeInfo || { | ||
edge: undefined, | ||
startNode: undefined, | ||
endNode: undefined, | ||
}; | ||
|
||
useEffect(() => { }, [edge, startNode, endNode]); | ||
|
||
return ( | ||
<Tabs className="common-info-panel"> | ||
{ | ||
props.children ? ( | ||
<Tabs.TabPane tab={'Summary'} key={'summary'}> | ||
{props.children} | ||
</Tabs.TabPane> | ||
) : <Empty /> | ||
} | ||
</Tabs> | ||
); | ||
}; | ||
|
||
export default CommonPanel; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import React, { useEffect } from 'react'; | ||
import { Tabs } from 'antd'; | ||
import type { EdgeInfo } from './index.t'; | ||
|
||
import './index.less'; | ||
|
||
type DiseaseGenePanelProps = { | ||
edgeInfo?: EdgeInfo; | ||
children?: React.ReactNode; | ||
}; | ||
|
||
const DiseaseGenePanel: React.FC<DiseaseGenePanelProps> = (props) => { | ||
const { edge, startNode, endNode } = props.edgeInfo || { | ||
edge: undefined, | ||
startNode: undefined, | ||
endNode: undefined, | ||
}; | ||
|
||
useEffect(() => { | ||
console.log('GeneDiseasePanel: ', edge, startNode, endNode); | ||
}, [edge, startNode, endNode]); | ||
|
||
return ( | ||
<Tabs className="gene-disease-info-panel tabs-nav-right"> | ||
{props.children ? ( | ||
<Tabs.TabPane tab={'Summary'} key={'summary'}> | ||
{props.children} | ||
</Tabs.TabPane> | ||
) : null} | ||
<Tabs.TabPane tab={'GeneDiease Info'} key={'gene-disease-info'}> | ||
We can show the gene-disease association information here. Maybe it's summarized information | ||
from publications. | ||
</Tabs.TabPane> | ||
<Tabs.TabPane tab={'Diff Expression'} key={'diff-expr'}> | ||
we can show the diff expression here. It can tell us whether the gene is up-regulated or | ||
down-regulated in the disease. | ||
</Tabs.TabPane> | ||
<Tabs.TabPane tab={'Biomarkers'} key={'biomarker'}> | ||
we can show the related biomarkers. It can tell us which genes are the biomarkers of the | ||
disease. | ||
</Tabs.TabPane> | ||
</Tabs> | ||
); | ||
}; | ||
|
||
export default DiseaseGenePanel; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React, { useEffect } from 'react'; | ||
import { Tabs } from 'antd'; | ||
import type { EdgeInfo } from './index.t'; | ||
|
||
import './index.less'; | ||
|
||
type DrugDiseasePanel = { | ||
edgeInfo?: EdgeInfo; | ||
children?: React.ReactNode; | ||
}; | ||
|
||
const DrugDiseasePanel: React.FC<DrugDiseasePanel> = (props) => { | ||
const { edge, startNode, endNode } = props.edgeInfo || { | ||
edge: undefined, | ||
startNode: undefined, | ||
endNode: undefined, | ||
}; | ||
|
||
useEffect(() => { }, [edge, startNode, endNode]); | ||
|
||
return ( | ||
<Tabs className="drug-disease-info-panel tabs-nav-right"> | ||
{props.children ? ( | ||
<Tabs.TabPane tab={'Summary'} key={'summary'}> | ||
{props.children} | ||
</Tabs.TabPane> | ||
) : null} | ||
<Tabs.TabPane tab={'DrugDisease Info'} key={'drug-disease-info'}> | ||
We can show the drug-disease association information here. Maybe it's summarized information | ||
from clinical trials, or publications. | ||
</Tabs.TabPane> | ||
<Tabs.TabPane tab={'Patents'} key={'drug-patent-info'}> | ||
We can show the patents information here. Maybe it's summarized information from patents | ||
database. | ||
</Tabs.TabPane> | ||
<Tabs.TabPane tab={'Products'} key={'drug-product-info'}> | ||
We can show the production information here. Maybe it's summarized information from drug | ||
production database. | ||
</Tabs.TabPane> | ||
</Tabs> | ||
); | ||
}; | ||
|
||
export default DrugDiseasePanel; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import React, { useEffect } from 'react'; | ||
import { Tabs } from 'antd'; | ||
import type { EdgeInfo } from './index.t'; | ||
|
||
import './index.less'; | ||
|
||
type DrugGenePanelProps = { | ||
edgeInfo?: EdgeInfo; | ||
children?: React.ReactNode; | ||
}; | ||
|
||
const DrugGenePanel: React.FC<DrugGenePanelProps> = (props) => { | ||
const { edge, startNode, endNode } = props.edgeInfo || { | ||
edge: undefined, | ||
startNode: undefined, | ||
endNode: undefined, | ||
}; | ||
|
||
useEffect(() => { }, [edge, startNode, endNode]); | ||
|
||
return ( | ||
<Tabs className="drug-gene-info-panel"> | ||
{props.children ? ( | ||
<Tabs.TabPane tab={'Summary'} key={'summary'}> | ||
{props.children} | ||
</Tabs.TabPane> | ||
) : null} | ||
<Tabs.TabPane tab={'DrugGene Info'} key={'drug-gene-info'}> | ||
We can show the drug-gene association information here. Maybe it's summarized information | ||
from publications. | ||
</Tabs.TabPane> | ||
<Tabs.TabPane tab={'Drug Targets'} key={'clinical-trails'}> | ||
Comming soon... | ||
</Tabs.TabPane> | ||
</Tabs> | ||
); | ||
}; | ||
|
||
export default DrugGenePanel; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import React, { useState } from 'react'; | ||
import { Button } from 'antd'; | ||
import parse from 'html-react-parser'; | ||
import type { PublicationDetail } from 'biominer-components/dist/typings'; | ||
|
||
export const SEPARATOR = '#'; | ||
|
||
const Desc: React.FC<{ | ||
publication: PublicationDetail, | ||
showAbstract: (doc_id: string) => Promise<PublicationDetail>, | ||
showPublication: (publication: PublicationDetail) => void, | ||
queryStr: string | ||
}> = (props) => { | ||
const { publication } = props; | ||
const [abstract, setAbstract] = useState<string>(''); | ||
const [abstractVisible, setAbstractVisible] = useState<boolean>(false); | ||
|
||
const fetchAbstract = (doc_id: string) => { | ||
props.showAbstract(doc_id).then((publication) => { | ||
console.log('fetchAbstract for a publication: ', publication); | ||
setAbstract(publication.article_abstract || ''); | ||
setAbstractVisible(true); | ||
}).catch((error) => { | ||
console.error('Error: ', error); | ||
setAbstract(''); | ||
setAbstractVisible(false); | ||
}); | ||
}; | ||
|
||
const escapeRegExp = (str: string) => { | ||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | ||
} | ||
|
||
const highlightWords = (text: string, words: string[]): string => { | ||
let newText = text; | ||
words.forEach(word => { | ||
let escapedWord = escapeRegExp(word); | ||
let regex = new RegExp(`(${escapedWord})(?![^<]*>|[^<>]*<\/)`, 'gi'); | ||
newText = newText.replace(regex, '<span class="highlight">$1</span>'); | ||
}); | ||
|
||
return newText; | ||
} | ||
|
||
return ( | ||
<div> | ||
<p> | ||
{parse(highlightWords(publication.summary, props.queryStr.split(SEPARATOR)))} | ||
<Button type="link" onClick={() => { | ||
if (abstractVisible) { | ||
setAbstractVisible(false); | ||
} else { | ||
fetchAbstract(publication.doc_id); | ||
} | ||
}} style={{ paddingLeft: '2px' }}> | ||
{abstractVisible ? 'Hide Abstract' : 'Show Abstract'} | ||
</Button> | ||
</p> | ||
{ | ||
abstractVisible ? | ||
<p>{parse(highlightWords(abstract, props.queryStr.split(SEPARATOR)))}</p> : null | ||
} | ||
<p> | ||
{publication.year} | {publication.journal} | {publication.authors ? publication.authors.join(', ') : 'Unknown'} | ||
</p> | ||
{ | ||
<p> | ||
Cited by {publication.citation_count ? publication.citation_count : 0} publications | | ||
<a onClick={(e) => { props.showPublication(publication) }}>View Publication</a> | ||
</p> | ||
} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Desc; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import { Button, List, message } from 'antd'; | ||
import { FileProtectOutlined } from '@ant-design/icons'; | ||
import type { Publication, PublicationDetail } from 'biominer-components/dist/typings'; | ||
import PublicationDesc from './PublicationDesc'; | ||
import { fetchPublication, fetchPublications } from '@/services/swagger/KnowledgeGraph'; | ||
|
||
import './index.less'; | ||
|
||
export type PublicationPanelProps = { | ||
queryStr: string; | ||
}; | ||
|
||
const PublicationPanel: React.FC<PublicationPanelProps> = (props) => { | ||
const [publications, setPublications] = useState<Publication[]>([]); | ||
const [page, setPage] = useState<number>(0); | ||
const [total, setTotal] = useState<number>(0); | ||
const [pageSize, setPageSize] = useState<number>(10); | ||
const [loading, setLoading] = useState<boolean>(false); | ||
const [publicationMap, setPublicationMap] = useState<Record<string, PublicationDetail>>({}); | ||
|
||
const showAbstract = (doc_id: string): Promise<PublicationDetail> => { | ||
console.log('Show Abstract: ', doc_id); | ||
return new Promise((resolve, reject) => { | ||
fetchPublication({ id: doc_id }).then((publication) => { | ||
console.log('Publication: ', publication); | ||
setPublicationMap({ | ||
...publicationMap, | ||
[doc_id]: publication | ||
}) | ||
resolve(publication); | ||
}).catch((error) => { | ||
console.error('Error: ', error); | ||
reject(error); | ||
}); | ||
}); | ||
}; | ||
|
||
useEffect(() => { | ||
if (!props.queryStr) { | ||
return; | ||
} | ||
|
||
setLoading(true); | ||
fetchPublications( | ||
{ | ||
query_str: props.queryStr, | ||
page: 0, | ||
page_size: 10 | ||
}).then((publications) => { | ||
setPublications(publications.records); | ||
setPage(publications.page); | ||
setTotal(publications.total); | ||
setPageSize(publications.page_size); | ||
}).catch((error) => { | ||
console.error('Error: ', error); | ||
message.error('Failed to fetch publications'); | ||
}).finally(() => { | ||
setLoading(false); | ||
}); | ||
}, [props.queryStr, page, pageSize]); | ||
|
||
const showPublication = async (publication: PublicationDetail) => { | ||
console.log('Show Publication: ', publication); | ||
if (publication) { | ||
console.log('Publication Map: ', publicationMap); | ||
const link = publication?.provider_url; | ||
const doi_link = "https://doi.org/" + publication?.doi; | ||
|
||
if (publication?.doi) { | ||
window.open(doi_link, '_blank'); | ||
} else if (link) { | ||
window.open(link, '_blank'); | ||
} else { | ||
message.warning('No link available for this publication'); | ||
} | ||
} | ||
}; | ||
|
||
const onClickPublication = (item: Publication) => { | ||
if (publicationMap[item.doc_id]) { | ||
showPublication(publicationMap[item.doc_id]) | ||
} else { | ||
showAbstract(item.doc_id).then((publication) => { | ||
showPublication(publication); | ||
}).catch((error) => { | ||
message.error('Failed to fetch publication details'); | ||
}); | ||
} | ||
} | ||
|
||
return ( | ||
<> | ||
<div className='publication-panel-header'> | ||
<h3> | ||
Relevant Publications | ||
</h3> | ||
<span>Keywords: {props.queryStr.split('#').join(', ')}</span> | ||
</div> | ||
<List | ||
loading={loading} | ||
itemLayout="horizontal" | ||
rowKey={'doc_id'} | ||
dataSource={publications} | ||
size="large" | ||
pagination={{ | ||
disabled: true, | ||
position: 'top', | ||
current: page, | ||
total: total, | ||
pageSize: pageSize, | ||
onChange: (page: number, pageSize: number) => { | ||
setPage(page); | ||
setPageSize(pageSize); | ||
} | ||
}} | ||
renderItem={(item, index) => ( | ||
<List.Item> | ||
<List.Item.Meta | ||
avatar={<FileProtectOutlined />} | ||
title={<a onClick={(e) => { onClickPublication(item); }}>{item.title}</a>} | ||
description={ | ||
<PublicationDesc publication={item} | ||
showAbstract={showAbstract} queryStr={props.queryStr} | ||
showPublication={(publication) => onClickPublication(publication)} | ||
/> | ||
} | ||
/> | ||
</List.Item> | ||
)} | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
export default PublicationPanel; |
Oops, something went wrong.