-
Notifications
You must be signed in to change notification settings - Fork 1
UI release #256
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
UI release #256
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,7 +35,7 @@ export const BEDEmbeddingView = (props: Props) => { | |||||||||
| const [selectedPoints, setSelectedPoints] = useState<any[]>([]); | ||||||||||
| const [initialPoint, setInitialPoint] = useState<any>(null); | ||||||||||
| const [viewportState, setViewportState] = useState<any>(null); | ||||||||||
| const [legendItems, setLegendItems] = useState<string[]>([]); | ||||||||||
| const [legendItems, setLegendItems] = useState<any[]>([]); | ||||||||||
| const [filterSelection, setFilterSelection] = useState<any>(null); | ||||||||||
| const [addedToCart, setAddedToCart] = useState(false); | ||||||||||
| const [tooltipPoint, setTooltipPoint] = useState<any>(null); | ||||||||||
|
|
@@ -46,6 +46,18 @@ export const BEDEmbeddingView = (props: Props) => { | |||||||||
| const [pinnedCategories, setPinnedCategories] = useState<Set<any>>(new Set()); | ||||||||||
| const [pinGrouping, setPinGrouping] = useState<string>(colorGrouping); | ||||||||||
|
|
||||||||||
| const categoryColors = useMemo(() => { | ||||||||||
| const colors = [...tableau20, '#cccccc']; | ||||||||||
| if (legendItems) { | ||||||||||
| legendItems.forEach((item: any) => { | ||||||||||
| if (item.name === 'UNKNOWN' && item.category < 20) { | ||||||||||
| colors[item.category] = '#cccccc'; | ||||||||||
| } | ||||||||||
| }); | ||||||||||
| } | ||||||||||
| return colors; | ||||||||||
| }, [legendItems]); | ||||||||||
|
|
||||||||||
| const filter = useMemo(() => vg.Selection.intersect(), []); | ||||||||||
| const legendFilterSource = useMemo(() => ({}), []); | ||||||||||
| const neighborIDs = useMemo(() => neighbors?.results?.map((result) => result.id), [neighbors]); | ||||||||||
|
|
@@ -350,11 +362,15 @@ export const BEDEmbeddingView = (props: Props) => { | |||||||||
| }; | ||||||||||
|
|
||||||||||
| const fetchLegendItems = async (coordinator: any) => { | ||||||||||
| const query = `SELECT DISTINCT | ||||||||||
| ${colorGrouping.replace('_category', '')} as name, | ||||||||||
| ${colorGrouping} as category | ||||||||||
| FROM data | ||||||||||
| ORDER BY ${colorGrouping}`; | ||||||||||
| const fieldName = colorGrouping.replace('_category', ''); | ||||||||||
| const query = ` | ||||||||||
| SELECT | ||||||||||
| CASE WHEN ${colorGrouping} < 20 THEN ${fieldName} ELSE 'Other' END as name, | ||||||||||
| CASE WHEN ${colorGrouping} < 20 THEN ${colorGrouping} ELSE 20 END as category, | ||||||||||
| COUNT(*) as count | ||||||||||
| FROM data | ||||||||||
| GROUP BY 1, 2 | ||||||||||
| ORDER BY count DESC`; | ||||||||||
|
|
||||||||||
| const result = (await coordinator.query(query, { type: 'json' })) as any[]; | ||||||||||
| return result; | ||||||||||
|
|
@@ -546,7 +562,7 @@ export const BEDEmbeddingView = (props: Props) => { | |||||||||
| identifier='id' | ||||||||||
| text='name' | ||||||||||
| category={colorGrouping} | ||||||||||
| categoryColors={tableau20} | ||||||||||
| categoryColors={categoryColors} | ||||||||||
| additionalFields={{ Description: 'description', Assay: 'assay', 'Cell Line': 'cell_line' }} | ||||||||||
| height={embeddingHeight} | ||||||||||
| width={containerWidth} | ||||||||||
|
|
@@ -641,18 +657,40 @@ export const BEDEmbeddingView = (props: Props) => { | |||||||||
| </label> | ||||||||||
| </div> | ||||||||||
| </div> | ||||||||||
| {pinnedCategories.size > 0 && ( | ||||||||||
| {legendItems?.length > 0 && ( | ||||||||||
| <div className='card-header border-bottom py-1 px-2 d-flex justify-content-between align-items-center'> | ||||||||||
| <span className='text-xs text-muted'>{pinnedCategories.size} pinned</span> | ||||||||||
| <button | ||||||||||
| className='btn btn-outline-danger btn-xs' | ||||||||||
| onClick={() => { | ||||||||||
| setPinnedCategories(new Set()); | ||||||||||
| filter.update({ source: legendFilterSource, value: null, predicate: null }); | ||||||||||
| }} | ||||||||||
| > | ||||||||||
| Unpin All | ||||||||||
| </button> | ||||||||||
| {pinnedCategories.size > 0 && ( | ||||||||||
| <span className='text-xs text-muted'>{pinnedCategories.size} pinned</span> | ||||||||||
| )} | ||||||||||
| <span className='d-flex gap-1 ms-auto'> | ||||||||||
| {pinnedCategories.size < legendItems.length && ( | ||||||||||
| <button | ||||||||||
| className='btn btn-outline-primary btn-xs' | ||||||||||
| onClick={() => { | ||||||||||
| const allCategories = new Set(legendItems.map((item: any) => item.category)); | ||||||||||
| setPinnedCategories(allCategories); | ||||||||||
| setPinGrouping(colorGrouping); | ||||||||||
| const categories = Array.from(allCategories); | ||||||||||
| const predicate = vg.or(...categories.map((cat: any) => vg.eq(colorGrouping, cat))); | ||||||||||
|
||||||||||
| const predicate = vg.or(...categories.map((cat: any) => vg.eq(colorGrouping, cat))); | |
| const predicates = categories.map((cat: any) => vg.eq(colorGrouping, cat)); | |
| const predicate = | |
| predicates.length === 1 ? predicates[0] : vg.or(...predicates); |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -5,6 +5,7 @@ const API_BASE = import.meta.env.VITE_API_BASE || ''; | |||
| const EXAMPLE_URL = `${API_BASE}/bed/example`; | ||||
|
|
||||
| export const UMAP_URL = 'https://huggingface.co/databio/bedbase-umap/resolve/main/hg38_umap_3_13.json'; | ||||
| // export const UMAP_URL = `${window.location.origin}/feb08_3_13.json`; | ||||
|
||||
| // export const UMAP_URL = `${window.location.origin}/feb08_3_13.json`; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -42,11 +42,22 @@ export const MosaicCoordinatorProvider = ({ children }: { children: ReactNode }) | |||||||||||||||||||||||||||||||||
| unnest(nodes, recursive := true) | ||||||||||||||||||||||||||||||||||
| FROM read_json_auto('${UMAP_URL}')`, | ||||||||||||||||||||||||||||||||||
| vg.sql`CREATE OR REPLACE TABLE data AS | ||||||||||||||||||||||||||||||||||
| SELECT | ||||||||||||||||||||||||||||||||||
| *, | ||||||||||||||||||||||||||||||||||
| (DENSE_RANK() OVER (ORDER BY assay) - 1)::INTEGER AS assay_category, | ||||||||||||||||||||||||||||||||||
| (DENSE_RANK() OVER (ORDER BY cell_line) - 1)::INTEGER AS cell_line_category | ||||||||||||||||||||||||||||||||||
| FROM data` as any, | ||||||||||||||||||||||||||||||||||
| WITH assay_counts AS ( | ||||||||||||||||||||||||||||||||||
| SELECT assay, | ||||||||||||||||||||||||||||||||||
| (ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) - 1)::INTEGER as rank | ||||||||||||||||||||||||||||||||||
| FROM data GROUP BY assay | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| cell_line_counts AS ( | ||||||||||||||||||||||||||||||||||
| SELECT cell_line, | ||||||||||||||||||||||||||||||||||
| (ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) - 1)::INTEGER as rank | ||||||||||||||||||||||||||||||||||
| FROM data GROUP BY cell_line | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| SELECT d.*, | ||||||||||||||||||||||||||||||||||
| CASE WHEN ac.rank < 20 THEN ac.rank ELSE 20 END AS assay_category, | ||||||||||||||||||||||||||||||||||
| CASE WHEN cc.rank < 20 THEN cc.rank ELSE 20 END AS cell_line_category | ||||||||||||||||||||||||||||||||||
| FROM data d | ||||||||||||||||||||||||||||||||||
| JOIN assay_counts ac ON d.assay = ac.assay | ||||||||||||||||||||||||||||||||||
| JOIN cell_line_counts cc ON d.cell_line = cc.cell_line` as any, | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+60
|
||||||||||||||||||||||||||||||||||
| CASE WHEN ac.rank < 20 THEN ac.rank ELSE 20 END AS assay_category, | |
| CASE WHEN cc.rank < 20 THEN cc.rank ELSE 20 END AS cell_line_category | |
| FROM data d | |
| JOIN assay_counts ac ON d.assay = ac.assay | |
| JOIN cell_line_counts cc ON d.cell_line = cc.cell_line` as any, | |
| CASE | |
| WHEN ac.rank IS NULL OR ac.rank >= 20 THEN 20 | |
| ELSE ac.rank | |
| END AS assay_category, | |
| CASE | |
| WHEN cc.rank IS NULL OR cc.rank >= 20 THEN 20 | |
| ELSE cc.rank | |
| END AS cell_line_category | |
| FROM data d | |
| LEFT JOIN assay_counts ac ON d.assay IS NOT DISTINCT FROM ac.assay | |
| LEFT JOIN cell_line_counts cc ON d.cell_line IS NOT DISTINCT FROM cc.cell_line` as any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR description TODOs mention updating
pepdbagent__version__.pyand the changelog, but this PR only shows UI changes (UMAP logic + UI version bump). Either update the PR description to match or include the missing version/changelog changes so the release notes are accurate.