Skip to content

Commit

Permalink
Phenogrid fixes (#376)
Browse files Browse the repository at this point in the history
- upgrade all packages
- filter out and collect unmatched phenotypes
- make tooltip node links absolute, since phenogrid can be used from
another domain as a widget
- include source and target in cell tooltips
- add button to copy unmatched phenotype ids
  • Loading branch information
vincerubinetti authored Oct 10, 2023
1 parent 25ee249 commit 8ad3cad
Show file tree
Hide file tree
Showing 10 changed files with 656 additions and 831 deletions.
46 changes: 23 additions & 23 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
"test:e2e": "playwright test"
},
"dependencies": {
"@floating-ui/dom": "^1.5.2",
"@floating-ui/dom": "^1.5.3",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-brands-svg-icons": "^6.4.2",
"@fortawesome/free-regular-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/vue-fontawesome": "^3.0.3",
"@sentry/browser": "^7.68.0",
"@sentry/vue": "^7.68.0",
"@sentry/browser": "^7.73.0",
"@sentry/vue": "^7.73.0",
"@vueuse/core": "^10.4.1",
"apexcharts": "^3.42.0",
"body-scroll-lock": "^4.0.0-beta.0",
"apexcharts": "^3.43.0",
"body-scroll-lock": "4.0.0-beta.0",
"dom-to-image": "^2.6.0",
"lodash": "^4.17.21",
"micromark": "^4.0.0",
Expand All @@ -34,43 +34,43 @@
"vue": "^3.3.4",
"vue-gtag": "^2.0.1",
"vue-hotjar": "^1.4.0",
"vue-router": "^4.2.4",
"vue-router": "^4.2.5",
"vue-tippy": "^6.3.1",
"vue3-apexcharts": "^1.4.4",
"wicg-inert": "^3.1.2"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.1.0",
"@playwright/test": "^1.37.1",
"@rushstack/eslint-patch": "^1.3.3",
"@tsconfig/node18": "^18.2.1",
"@playwright/test": "~1.37.0",
"@rushstack/eslint-patch": "^1.5.1",
"@tsconfig/node18": "^18.2.2",
"@types/body-scroll-lock": "^3.1.0",
"@types/dom-to-image": "^2.6.5",
"@types/jsdom": "^21.1.2",
"@types/lodash": "^4.14.198",
"@types/node": "20",
"@types/jsdom": "^21.1.3",
"@types/lodash": "^4.14.199",
"@types/node": "20.8.2",
"@types/ua-parser-js": "^0.7.37",
"@vitejs/plugin-vue": "^4.3.4",
"@vitejs/plugin-vue": "^4.4.0",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^11.0.3",
"@vue/eslint-config-typescript": "^12.0.0",
"@vue/test-utils": "2.4.1",
"@vue/tsconfig": "^0.4.0",
"axe-playwright": "^1.2.3",
"eslint": "^8.49.0",
"eslint": "^8.51.0",
"eslint-plugin-vue": "^9.17.0",
"eslint-plugin-vuejs-accessibility": "^2.2.0",
"jsdom": "^22.1.0",
"msw": "^1.3.0",
"postcss": "^8.4.29",
"msw": "^1.3.2",
"postcss": "^8.4.31",
"prettier": "^3.0.3",
"prettier-plugin-css-order": "^2.0.0",
"prettier-plugin-jsdoc": "^1.0.2",
"sass": "^1.66.1",
"prettier-plugin-css-order": "^2.0.1",
"prettier-plugin-jsdoc": "^1.1.0",
"sass": "^1.69.0",
"typescript": "^5.2.2",
"vite": "^4.4.9",
"vite": "^4.4.11",
"vite-svg-loader": "jpkleemans/vite-svg-loader#v4.1.0",
"vitest": "^0.34.4",
"vue-tsc": "^1.8.10"
"vitest": "^0.34.6",
"vue-tsc": "^1.8.16"
},
"msw": {
"workerDirectory": "public"
Expand Down
2 changes: 1 addition & 1 deletion frontend/public/mockServiceWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* tslint:disable */

/**
* Mock Service Worker (1.3.0).
* Mock Service Worker (1.3.2).
* @see https://github.com/mswjs/msw
* - Please do NOT modify this file.
* - Please do NOT serve this file on production.
Expand Down
46 changes: 32 additions & 14 deletions frontend/src/api/phenotype-explorer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { pick } from "lodash";
import { pick, uniqBy } from "lodash";
import type {
AssociationResults,
TermPairwiseSimilarity,
Expand Down Expand Up @@ -124,12 +124,12 @@ export const compareSetToSet = async (
summary.sort((a, b) => b.score - a.score);

/** turn objects into array of cols */
const cols = Object.values(response.object_termset || {}).map((col) => ({
let cols = Object.values(response.object_termset || {}).map((col) => ({
...col,
total: 0,
}));
/** turn subjects into array of cols */
const rows = Object.values(response.subject_termset || {}).map((row) => ({
let rows = Object.values(response.subject_termset || {}).map((row) => ({
...row,
total: 0,
}));
Expand All @@ -149,21 +149,20 @@ export const compareSetToSet = async (
for (const col of cols) {
for (const row of rows) {
/** find match corresponding to col/row id */
const { score = 0, similarity = {} } =
matches.find(
({ match_source, match_target }) =>
match_source === row.id && match_target === col.id,
) || {};
const match = matches.find(
({ match_source, match_target }) =>
match_source === row.id && match_target === col.id,
);

/** sum up row and col scores */
col.total += score;
row.total += score;
col.total += match?.score || 0;
row.total += match?.score || 0;

/** assign cell */
cells[col.id + row.id] = {
score: score || 0,
score: match?.score || 0,
strength: 0,
simInfo: pick(similarity, [
simInfo: pick(match?.similarity, [
"ancestor_id",
"jaccard_similarity",
"phenodigm_score",
Expand All @@ -172,15 +171,34 @@ export const compareSetToSet = async (
}
}

/** collect unmatched phenotypes */
let unmatched: typeof cols = [];

/** filter out unmatched phenotypes */
cols = cols.filter((col) => {
col.total && unmatched.push(col);
return col.total;
});
rows = rows.filter((row) => {
row.total && unmatched.push(row);
return row.total;
});

/** deduplicate unmatched phenotypes */
unmatched = uniqBy(unmatched, "id");

/** normalize cell scores to 0-1 */
const scores = Object.values(cells).map((value) => value.score);
const min = Math.min(...scores);
const max = Math.max(...scores);
Object.values(cells).forEach(
(value) => (value.strength = (value.score - min) / (max - min)),
(value) => (value.strength = (value.score - min) / (max - min || 0)),
);

return { summary, phenogrid: { cols, rows, cells } };
/** assemble all data needed for phenogrid */
const phenogrid = { cols, rows, cells, unmatched };

return { summary, phenogrid };
};

export type SetToSet = Awaited<ReturnType<typeof compareSetToSet>>;
Expand Down
7 changes: 1 addition & 6 deletions frontend/src/components/AppButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
:class="['button', design, color, { text }]"
:to="to"
:type="type"
@click="copy ? copyFunc() : click"
@click="copy ? copyToClipboard(text) : click"
>
<span v-if="text" class="truncate">{{ text }}</span>
<AppIcon v-if="icon" :icon="icon" />
Expand Down Expand Up @@ -53,11 +53,6 @@ const props = withDefaults(defineProps<Props>(), {
/** element ref */
const button = ref();

/** copy text prop to clipboard */
async function copyFunc() {
copyToClipboard(props.text);
}

/** type of component to render */
const component = computed(() => (props.to ? "AppLink" : "button"));

Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/AppNodeBadge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
/>
<AppLink
v-if="!currentPage"
:to="`/${node.id}`"
:to="`${absolute ? baseurl : ''}${node.id}`"
:state="
breadcrumbs
? { breadcrumbs: [...currentBreadcrumbs, ...breadcrumbs] }
Expand All @@ -33,6 +33,8 @@ import type { Node } from "@/api/model";
import { breadcrumbs as currentBreadcrumbs } from "@/global/breadcrumbs";
import type { Breadcrumb } from "@/global/breadcrumbs";

const { VITE_URL: baseurl } = import.meta.env;

type Props = {
/** node represented by badge */
node: Partial<Node> & {
Expand All @@ -48,13 +50,16 @@ type Props = {
breadcrumbs?: Breadcrumb[];
/** state data to pass through to link component */
state?: { [key: string]: unknown };
/** whether to use absolute link */
absolute?: boolean;
};

const props = withDefaults(defineProps<Props>(), {
icon: true,
link: true,
breadcrumbs: undefined,
state: undefined,
absolute: false,
});

/** whether we're already on page we're linking to */
Expand Down
56 changes: 45 additions & 11 deletions frontend/src/components/ThePhenogrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
{{ col.label }}
</div>
<template #content>
<AppNodeBadge :node="{ id: col.id, name: col.label }" /> ({{
col.id
}})
<AppNodeBadge
:node="{ id: col.id, name: col.label }"
:absolute="true"
/>
({{ col.id }})
</template>
</tooltip>
</tr>
Expand All @@ -48,9 +50,11 @@
>
{{ row.label }}
<template #content>
<AppNodeBadge :node="{ id: row.id, name: row.label }" /> ({{
row.id
}})
<AppNodeBadge
:node="{ id: row.id, name: row.label }"
:absolute="true"
/>
({{ row.id }})
</template>
</tooltip>

Expand All @@ -71,6 +75,16 @@
>
<template #content>
<div class="mini-table">
<AppNodeBadge
class="span"
:node="{ id: col.id, name: col.label }"
:absolute="true"
/>
<AppNodeBadge
class="span"
:node="{ id: row.id, name: row.label }"
:absolute="true"
/>
<span>Score</span>
<span>{{
data.cells[col.id + row.id].score.toFixed(2)
Expand Down Expand Up @@ -104,7 +118,7 @@
</AppFlex>

<AppFlex>
<tooltip :interactive="true" :append-to="appendToBody" tag="button">
<!-- <tooltip :interactive="true" :append-to="appendToBody" tag="button">
Info&nbsp;<AppIcon icon="circle-question" />
<template #content>
<p>
Expand All @@ -116,7 +130,7 @@
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</template>
</tooltip>
</tooltip> -->
<AppButton
v-tooltip="'Download grid as PNG'"
design="small"
Expand All @@ -133,6 +147,13 @@
/>
</AppFlex>
<AppCheckbox v-model="reverse" text="Flip" />
<AppButton
v-tooltip="'Copy unmatched phenotype ids to clipboard'"
design="small"
icon="copy"
:text="`Unmatched (${data.unmatched.length})`"
@click="copy"
/>
</AppFlex>
</AppFlex>
</template>
Expand All @@ -149,15 +170,17 @@ import AppSelectSingle, {
type Option,
type Options,
} from "@/components/AppSelectSingle.vue";
import { snackbar } from "@/components/TheSnackbar.vue";
import { appendToBody } from "@/global/tooltip";
import { frame, sleep } from "@/util/debug";
import { downloadPng, getScreenshot } from "@/util/download";
import { copyToClipboard } from "@/util/string";
type Props = {
data: SetToSet["phenogrid"];
};
defineProps<Props>();
const props = defineProps<Props>();
const hovered = ref<{ col: number; row: number }>();
Expand Down Expand Up @@ -190,7 +213,7 @@ async function download() {
downloadPng(png, "phenogrid");
} catch (error) {
console.error(error);
window.alert("Error saving image");
snackbar("Error saving image");
}
/** reset size */
Expand All @@ -216,12 +239,19 @@ function sort(array: TermInfo[]): TermInfo[] {
if (reverse.value) array.reverse();
return array;
}
/** copy unmatched phenotype ids to clipboard */
function copy() {
copyToClipboard(
props.data.unmatched.map((phenotype) => phenotype.id).join(","),
);
}
</script>

<style scoped lang="scss">
.scroll {
max-width: 100%;
max-height: 500px;
max-height: calc(100vh - 200px);
overflow: auto;
background: $white;
}
Expand Down Expand Up @@ -324,4 +354,8 @@ td {
.hovered {
font-weight: 600;
}
.span {
grid-column: span 2;
}
</style>
Loading

0 comments on commit 8ad3cad

Please sign in to comment.