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

Add lineages to results table #23

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
35 changes: 33 additions & 2 deletions app/client/src/components/ResultsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@
<th>Sketch</th>
<th>AMR</th>
<th>Cluster</th>
<th class="dropdown">
<div class="dropdown-toggle"
type="button" id="dropdownMenuButton"
data-bs-toggle="dropdown"
aria-expanded="false">
Lineage Rank {{rank}}
</div>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" id="one" @click="onInput(1)" href="#">Rank 1</a>
<a class="dropdown-item" id="two" @click="onInput(2)" href="#">Rank 2</a>
<a class="dropdown-item" id="three" @click="onInput(3)" href="#">Rank 3</a>
</div>
</th>
<th>Microreact</th>
<th>Network</th>
</thead>
Expand All @@ -29,6 +42,9 @@
<td :class="(typeof sample.Cluster === 'number') ? '' : 'processing'">
{{sample.Cluster}}
</td>
<td :class="(typeof sample.Lineage === 'number') ? '' : 'processing'">
{{sample.Lineage}}
</td>
<td v-if="sample.Rowspan !== 0"
style="vertical-align : middle;"
:rowspan="sample.Rowspan"
Expand Down Expand Up @@ -77,6 +93,11 @@ export default defineComponent({
DownloadZip,
GenerateMicroreactURL,
},
data() {
return {
rank: 1,
};
},
methods: {
getRGB,
tooltipLine,
Expand All @@ -91,14 +112,23 @@ export default defineComponent({
},
getCluster(sample: string) {
return (this.results.perIsolate[sample].cluster
? this.results.perIsolate[sample].cluster : this.analysisStatus.assign);
? this.results.perIsolate[sample].cluster : this.analysisStatus.assignClusters);
},
getLineage(sample: string, rank: number) {
const selectedRank = `rank${rank}`;
return (this.results.perIsolate[sample].lineage
? this.results.perIsolate[sample].lineage[selectedRank]
: this.analysisStatus.assignLineages);
},
getMicroreact() {
return (this.analysisStatus.microreact === 'finished') ? 'showButton' : this.analysisStatus.microreact;
},
getNetwork() {
return (this.analysisStatus.network === 'finished') ? 'showButton' : this.analysisStatus.network;
},
onInput(value: number) {
this.rank = value;
},
},
computed: {
...mapState(['results', 'submitStatus', 'analysisStatus']),
Expand All @@ -112,12 +142,13 @@ export default defineComponent({
Sketch: (samples[sample].sketch) ? '\u2714' : 'processing',
AMR: samples[sample].amr ? samples[sample].amr : 'processing',
Cluster: this.submitStatus === 'submitted' ? this.getCluster(sample) : '',
Lineage: this.submitStatus === 'submitted' ? this.getLineage(sample, this.rank) : '',
Microreact: this.submitStatus === 'submitted' ? this.getMicroreact() : '',
Network: this.submitStatus === 'submitted' ? this.getNetwork() : '',
});
});
const tableSorted = items.sort((a, b) => Number(a.Cluster) - Number(b.Cluster));
if (this.analysisStatus.assign === 'finished') {
if (this.analysisStatus.assignClusters === 'finished') {
// adding a rowspan property to merge microreact/ network cells from same cluster
const tableRowspan = addRowspan(tableSorted);
return tableRowspan;
Expand Down
14 changes: 14 additions & 0 deletions app/client/src/scss/myStyles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,18 @@ small {
border-left: 0;
border-top: 0;
border-bottom: 0;
}

.dropdown {
position: relative;
display: flex;
}

.dropdown-toggle {
flex-grow: 1;
padding: 0 !important;
}

.dropdown-menu {
background-color: white !important;
}
31 changes: 24 additions & 7 deletions app/client/src/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,31 @@ export default {
names: filenameMapping,
});
if (response) {
commit('setAnalysisStatus', { assign: 'submitted', microreact: 'submitted', network: 'submitted' });
commit('setAnalysisStatus', {
assignClusters: 'submitted',
assignLineages: 'submitted',
microreact: 'submitted',
network: 'submitted',
});
}
},
async getStatus(context: ActionContext<RootState, RootState>) {
const { state, dispatch } = context;
const prevAssign = state.analysisStatus.assign;
const prevAssignClusters = state.analysisStatus.assignClusters;
const prevAssignLineages = state.analysisStatus.assignLineages;
const response = await api(context)
.withSuccess('setAnalysisStatus')
.withError('addError')
.post<AnalysisStatus>(`${serverUrl}/status`, { hash: state.projectHash });
if (response) {
if (response.data.assign === 'finished' && prevAssign !== 'finished') {
dispatch('getAssignResult');
if (response.data.assignClusters === 'finished' && prevAssignClusters !== 'finished') {
dispatch('getAssignClustersResult');
}
if (response.data.assignLineages === 'finished' && prevAssignLineages !== 'finished') {
dispatch('getAssignLineagesResult');
}
if ((response.data.network === 'finished' || response.data.network === 'failed')
if ((response.data.assignLineages === 'finished' || response.data.assignLineages === 'failed')
&& (response.data.network === 'finished' || response.data.network === 'failed')
&& (response.data.microreact === 'finished' || response.data.microreact === 'failed')) {
clearInterval(state.statusInterval);
}
Expand All @@ -100,12 +110,19 @@ export default {
clearInterval(state.statusInterval);
}
},
async getAssignResult(context: ActionContext<RootState, RootState>) {
async getAssignClustersResult(context: ActionContext<RootState, RootState>) {
const { state } = context;
await api(context)
.withSuccess('setClusters')
.withError('addError')
.post<ClusterInfo>(`${serverUrl}/assignResult`, { projectHash: state.projectHash });
.post<ClusterInfo>(`${serverUrl}/assignClustersResult`, { projectHash: state.projectHash });
},
async getAssignLineagesResult(context: ActionContext<RootState, RootState>) {
const { state } = context;
await api(context)
.withSuccess('setLineages')
.withError('addError')
.post<ClusterInfo>(`${serverUrl}/assignLineagesResult`, { projectHash: state.projectHash });
},
async startStatusPolling(context: ActionContext<RootState, RootState>) {
const { dispatch, commit } = context;
Expand Down
3 changes: 2 additions & 1 deletion app/client/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export default new Vuex.Store<RootState>({
projectHash: null,
submitStatus: null,
analysisStatus: {
assign: null,
assignClusters: null,
assignLineages: null,
microreact: null,
network: null,
},
Expand Down
18 changes: 14 additions & 4 deletions app/client/src/store/mutations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RootState } from '@/store/state';
import {
Versions, User, IsolateValue, AnalysisStatus, ClusterInfo, BeebopError,
Versions, User, IsolateValue, AnalysisStatus, ClusterInfo, BeebopError, Dict,
} from '@/types';

export default {
Expand Down Expand Up @@ -43,9 +43,19 @@ export default {
state.statusInterval = interval;
},
setClusters(state: RootState, clusterInfo: ClusterInfo) {
Object.keys(clusterInfo).forEach((element) => {
state.results.perIsolate[clusterInfo[element].hash]
.cluster = clusterInfo[element].cluster;
Object.keys(clusterInfo).forEach((cluster) => {
clusterInfo[cluster].forEach((hash) => {
state.results.perIsolate[hash].cluster = cluster;
});
});
},
setLineages(state: RootState, lineageInfo: Dict<Dict<string>>) {
Object.keys(lineageInfo).forEach((hash) => {
state.results.perIsolate[hash].lineage = {
rank1: lineageInfo[hash].rank1,
rank2: lineageInfo[hash].rank2,
rank3: lineageInfo[hash].rank3,
};
});
},
addMicroreactURL(state: RootState, URLinfo: Record<string, string>) {
Expand Down
6 changes: 4 additions & 2 deletions app/client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface Isolate {
amr?: AMR
sketch?: string
cluster?: number | string
lineage?: Dict<string>
}

export interface Versions {
Expand Down Expand Up @@ -37,7 +38,8 @@ export interface IsolateValue {
}

export enum AnalysisType {
ASSIGN = 'assign',
ASSIGNCLUSTERS = 'assignClusters',
ASSIGNLINEAGES = 'assignLineages',
MICROREACT = 'microreact',
NETWORK = 'network'
}
Expand All @@ -46,7 +48,7 @@ export type AnalysisStatus = {
[key in AnalysisType]: string | null
}

export type ClusterInfo = Dict<Dict<string>>
export type ClusterInfo = Dict<string[]>

export interface BeebopError {
error: string,
Expand Down
8 changes: 8 additions & 0 deletions app/client/tests/e2e/LoggedIn.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ test.describe('Logged in Tests', () => {
// Expect clusters appearing in table
await expect(page.locator('tr:has-text("6930_8_13.fa")')).toContainText(['6930_8_13.fa', '✔', 'PCETE SXT', '7']);
await expect(page.locator('tr:has-text("6930_8_11.fa")')).toContainText(['6930_8_11.fa', '✔', 'PCETE SXT', '24']);
// Expect lineages to appear in table
await expect(page.locator('tr:has-text("6930_8_13.fa")')).toContainText(['6930_8_13.fa', '✔', 'PCETE SXT', '7', '37']);
await expect(page.locator('tr:has-text("6930_8_11.fa")')).toContainText(['6930_8_11.fa', '✔', 'PCETE SXT', '24', '12']);
// Changing rank updates lineages
await page.click('text=Lineage Rank 1');
await page.click('text=Rank 2');
await expect(page.locator('tr:has-text("6930_8_13.fa")')).toContainText(['6930_8_13.fa', '✔', 'PCETE SXT', '7', '6']);
await expect(page.locator('tr:has-text("6930_8_11.fa")')).toContainText(['6930_8_11.fa', '✔', 'PCETE SXT', '24', '2']);
// Expect download buttons and button to generate microreact URL to appear
await expect(page.locator('tr:has-text("6930_8_13.fa") .btn').nth(0)).toContainText('Download zip file');
await expect(page.locator('tr:has-text("6930_8_13.fa") .btn').nth(1)).toContainText('Generate Microreact URL');
Expand Down
2 changes: 1 addition & 1 deletion app/client/tests/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function mockRootState(state: Partial<RootState> = {}): RootState {
},
submitStatus: null,
analysisStatus: {
assign: null, microreact: null, network: null,
assignClusters: null, assignLineages: null, microreact: null, network: null,
},
statusInterval: undefined,
projectHash: null,
Expand Down
16 changes: 9 additions & 7 deletions app/client/tests/unit/components/ProgressBar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { mockRootState } from '../../mocks';
describe('Progress bar', () => {
const analysisProgress = jest.fn().mockReturnValue({
finished: 1,
progress: 0.3333333333333333,
total: 3,
progress: 0.25,
total: 4,
});

const store = new Vuex.Store<RootState>({
state: mockRootState({
analysisStatus: {
assign: 'finished',
assignClusters: 'finished',
assignLineages: 'queued',
microreact: 'queued',
network: 'started',
},
Expand All @@ -37,21 +38,22 @@ describe('Progress bar', () => {
it('gets analysisProgress and displays status', () => {
expect(analysisProgress).toHaveBeenCalledTimes(1);
expect(wrapper.vm.animated).toBe(true);
expect(wrapper.find('.progress-bar').text()).toBe('33.33%');
expect(wrapper.find('.progress-bar').text()).toBe('25.00%');
});
});

describe('Progress bar finished', () => {
const analysisProgress = jest.fn().mockReturnValue({
finished: 3,
finished: 4,
progress: 1,
total: 3,
total: 4,
});

const store = new Vuex.Store<RootState>({
state: mockRootState({
analysisStatus: {
assign: 'finished',
assignClusters: 'finished',
assignLineages: 'finished',
microreact: 'finished',
network: 'finished',
},
Expand Down
Loading