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

[ALS-7585] Add optional sample Ids checkbox #297

Merged
merged 7 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ VITE_USE_QUERY_TEMPLATE=true
VITE_API=true
VITE_DISCOVER=true
VITE_DASHBOARD=true
VITE_ENABLE_SAMPLE_ID_CHECKBOX=true

# VITE_AUTH_PROVIDER_MODULE is the prefix for any authorization providers you want to use.
# We currently support 3 types of authorization providers: AUTH0, Gen3 FENCE, and RAS.
Expand Down
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ VITE_DISCOVER=true
VITE_DASHBOARD=true
VITE_ENABLE_SNP_QUERY=true
VITE_ENABLE_GENE_QUERY=true
VITE_ENABLE_SAMPLE_ID_CHECKBOX=true

# VITE_AUTH_PROVIDER_MODULE is the prefix for any authorization providers you want to use.
# We currently support 3 types of authorization providers: AUTH0, Gen3 FENCE, and RAS.
Expand Down
163 changes: 122 additions & 41 deletions src/lib/components/explorer/export/ExportStepper.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import * as api from '$lib/api';
import { v4 as uuidv4 } from 'uuid';
import { browser } from '$app/environment';
import Stepper from '$lib/components/steppers/horizontal/Stepper.svelte';
import Step from '$lib/components/steppers/horizontal/Step.svelte';
Expand All @@ -13,17 +14,19 @@
import ErrorAlert from '$lib/components/ErrorAlert.svelte';
import ExportStore from '$lib/stores/Export';
import { filters, totalParticipants } from '$lib/stores/Filter';
let { exports } = ExportStore;
let { exports, addExports, removeExports } = ExportStore;
import { state } from '$lib/stores/Stepper';
import { goto } from '$app/navigation';
import { type DatasetError } from '$lib/models/Dataset';
import { createDatasetName } from '$lib/services/datasets';
import CardButton from '$lib/components/buttons/CardButton.svelte';
import type { ExpectedResultType } from '$lib/models/query/Query.ts';
import { Query, type ExpectedResultType } from '$lib/models/query/Query.ts';
import codeBlocks from '$lib/assets/codeBlocks.json';
import { getModalStore, type ModalSettings } from '@skeletonlabs/skeleton';
import Confirmation from '$lib/components/modals/Confirmation.svelte';
import { branding, features, settings } from '$lib/configuration';
import { branding, features, settings, resources } from '$lib/configuration';
import { searchDictionary } from '$lib/services/dictionary';
import type { ExportInterface } from '$lib/models/Export';
export let query: QueryRequestInterface;
export let showTreeStep = false;
export let rows: ExportRowInterface[] = [];
Expand All @@ -35,6 +38,9 @@
let picsureResultId: string = '';
let lockDownload = true;
let error: string = '';
let sampleIds = false;
let lastExports: ExportRowInterface[] = [];
let loadingSampleIds = false;
$: datasetId = '';
$: canDownload = true;
$: apiExport = false;
Expand All @@ -61,7 +67,9 @@
modalClasses: 'bg-surface-100-800-token p-4 block',
meta: {
component: Confirmation,
message: branding.explorePage.confirmDownloadMessage,
message:
branding.explorePage.confirmDownloadMessage ||
'This action will download the data to your local machine. Are you sure you want to proceed?',
onConfirm,
onCancel,
confirmText: 'Download',
Expand Down Expand Up @@ -94,13 +102,11 @@
}
}
function onNextHandler(e: CustomEvent): void {
console.log('event:next', e);
if (e.detail.step === 0 && !showTreeStep) {
// nothing needs to be done here
return;
}
if (e.detail.step === 1 && showTreeStep) {
console.log('event:next', e);
//TODO: Load tree
} else if ((e.detail.step === 1 && !showTreeStep) || (showTreeStep && e.detail.step === 2)) {
preparePromise = submitQuery();
Expand Down Expand Up @@ -136,7 +142,6 @@
try {
query.query.fields = $exports.map((exp) => exp.conceptPath);
const res = await api.post('picsure/query', query);
console.log('res', res);
datasetId = res.picsureResultId;
} catch (error) {
$state.current--;
Expand Down Expand Up @@ -203,6 +208,88 @@
return totalDataPoints > MAX_DATA_POINTS_FOR_EXPORT;
}

async function toggleSampleIds() {
try {
loadingSampleIds = true;
if (!sampleIds) {
const exportsToRemove: ExportInterface[] = lastExports?.map(
(e) => e.ref,
) as ExportInterface[];
removeExports(exportsToRemove || []);
rows = rows.filter((r) => !lastExports.includes(r));
return;
}

// Get genomic concepts from dictionary
const concepts = await searchDictionary(
'',
[
{
name: 'data_source_genomic',
category: 'data_source',
display: 'Genomic',
description: 'Associated with genomic data',
count: 0,
},
],
{ pageNumber: 0, pageSize: 10000 },
);

if (concepts.content.length === 0) {
return;
}

// Get sample ID counts via cross counts query
const crossCountQuery = new Query(structuredClone(query.query));
crossCountQuery.expectedResultType = 'CROSS_COUNT';
const crossCountFields = concepts.content.map((concept) => concept.conceptPath);
crossCountQuery.setCrossCountFields(crossCountFields);

const crossCountResponse: Record<string, number> = await api.post('picsure/query/sync', {
query: crossCountQuery,
resourceUUID: resources.hpds,
});

// Filter to paths with counts > 0
const conceptPathsToAdd = Object.entries(crossCountResponse)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.filter(([_, count]) => count > 0)
.map(([path]) => path);

// Create new exports for each path
const newExports = conceptPathsToAdd.map((path) => {
const concept = concepts.content.find((c) => c.conceptPath === path);
return {
id: uuidv4(),
searchResult: concept,
display: concept?.display || '',
conceptPath: concept?.conceptPath || '',
} as ExportInterface;
});

// Add exports and create corresponding rows
addExports(newExports);
const newRows = newExports.map(
(e) =>
({
ref: e,
variableId: e.id,
variableName: e.display,
description: e?.searchResult?.description,
type: e?.searchResult.type,
selected: true,
}) as ExportRowInterface,
);

rows = [...rows, ...newRows];
lastExports = newRows;
} catch (error) {
console.error('Error in toggleSampleIds', error);
JamesPeck marked this conversation as resolved.
Show resolved Hide resolved
} finally {
loadingSampleIds = false;
}
}

async function exportToTerra() {
exportLoading = true;
let signedUrl = await getSignedUrl();
Expand Down Expand Up @@ -253,7 +340,33 @@
/>
</div>
{/await}
<Datatable tableName="ExportSummary" data={rows} {columns} />
{#if !loadingSampleIds}
<Datatable tableName="ExportSummary" data={rows} {columns} />
{#if features.explorer.enableSampleIdCheckbox}
<div>
<label
for="sample-ids-checkbox"
class="flex items-center"
data-testid="sample-ids-label"
>
<input
type="checkbox"
class="mr-1 &[aria-disabled=“true”]:opacity-75"
data-testid="sample-ids-checkbox"
id="sample-ids-checkbox"
bind:checked={sampleIds}
on:change={toggleSampleIds}
/>
<span>Include sample identifiers</span>
</label>
</div>
{/if}
{:else}
<div class="flex justify-center items-center">
<ProgressRadial width="w-4" />
<div>Loading sample IDs...</div>
</div>
{/if}
{/if}
</section>
</div>
Expand Down Expand Up @@ -414,38 +527,6 @@
>
</div>
</section>
<!--<section id="info-cards" class="w-full flex flex-wrap flex-row justify-center mt-6">
{#each branding.apiPage.cards as card}
<a
href={card.link}
target={card.link.startsWith('http') ? '_blank' : '_self'}
class="pic-sure-info-card p-4 basis-2/4"
>
<div class="card card-hover">
<header class="card-header flex flex-col items-center">
<h4 class="my-1" data-testid={card.header}>{card.header}</h4>
<hr class="!border-t-2" />
</header>
<section class="p-4 whitespace-pre-wrap flex flex-col" data-testid={card.body}>
<div>{card.body}</div>
<div class="flex justify-center">
<div class="btn variant-filled-primary mt-3 w-fit">Learn More</div>
</div>
</section>
</div>
</a>
{/each}
</section>-->
<!--<div class="flex flex-col items-center justify-center">
<div>
Export Status: {status}
<i
class="fa-solid {status === 'ERROR'
? 'fa-circle-xmark text-error-500'
: 'fa-check-circle text-success-500'}"
></i>
</div>
</div>-->
{:else if query.query.expectedResultType === 'DATAFRAME_PFB'}
<section class="flex flex-col gap-8">
<div class="flex justify-center mt-4">
Expand Down Expand Up @@ -513,7 +594,7 @@
</Stepper>

<style>
input {
input[type='text'] {
border-radius: var(--theme-rounded-base);
}
</style>
1 change: 1 addition & 0 deletions src/lib/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export const features: Indexable = {
authTour: import.meta.env?.VITE_OPEN_TOUR_NAME ?? 'NHANES-Auth',
enableHierarchy: import.meta.env?.VITE_ENABLE_HIERARCHY === 'true',
enableTerraExport: import.meta.env?.VITE_ENABLE_TERRA_EXPORT === 'true',
enableSampleIdCheckbox: import.meta.env?.VITE_ENABLE_SAMPLE_ID_CHECKBOX === 'true',
},
login: {
open: import.meta.env?.VITE_OPEN === 'true',
Expand Down
15 changes: 15 additions & 0 deletions src/lib/stores/Export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ function addExport(exportedField: ExportInterface) {
return exportedField;
}

function addExports(exportedFields: ExportInterface[]) {
const currentExports = get(exports);
const newExports = exportedFields.filter(
(e: ExportInterface) => !currentExports.some((ce: ExportInterface) => ce.id === e.id),
);
exports.set([...currentExports, ...newExports]);
}

function removeExport(uuid: string) {
const currentExports = get(exports);
exports.set(
Expand All @@ -22,6 +30,11 @@ function removeExport(uuid: string) {
);
}

function removeExports(exportsToRemove: ExportInterface[]) {
const currentExports = get(exports);
exports.set(currentExports.filter((e: ExportInterface) => !exportsToRemove.includes(e)));
}

export function clearExports() {
exports.set([]);
}
Expand All @@ -30,6 +43,8 @@ export default {
subscribe: exports.subscribe,
exports,
addExport,
addExports,
removeExport,
removeExports,
clearExports,
};