Skip to content

Instantly share code, notes, and snippets.

@CodingBash
Created January 31, 2026 13:05
Show Gist options
  • Select an option

  • Save CodingBash/fdb854a485e3186ec3d3ec7adcc799ce to your computer and use it in GitHub Desktop.

Select an option

Save CodingBash/fdb854a485e3186ec3d3ec7adcc799ce to your computer and use it in GitHub Desktop.
<div style="margin-top: -19px">
<div class="row" style="margin-left: -11px;">
<div class="col-xs-12">
<i class="text-center">{{this.numberOfFits}} total fits displayed.</i>
</div>
</div>
<!-- TODO Getting species/study count is ran twice. Create a pipe for it for optimization -->
<div class="row" style="margin-top: 0px;">
<div class="col-xs-12">
<mat-card style="padding-top: 10px;">
<!-- TODO: Fix portal margin-->
<mat-card-subtitle>
Species:
</mat-card-subtitle>
<mat-card-content>
<mat-list>
<mat-list-item *ngFor="let species of availableSpeciesList" #localVariable="">
<mat-checkbox [checked]="speciesStatusDictionary.get(species).checked"
[disabled]="speciesStatusDictionary.get(species).disabled || this.getTotalFitCount(this.filteredDataSource,
this.adminViewEnabled, species, undefined, undefined) == 0"
(change)="speciesCheckboxChange(species, $event.checked)">
<span
style="font-weight: 600 !important;"><i>{{this.speciesService.mapSpeciesNameFromTaxId(species).taxName}}</i></span>&nbsp;<i style="font-weight: 400 !important;">({{this.getTotalFitCount(this.filteredDataSource,
this.adminViewEnabled, species, undefined, undefined)}} fits)</i>
</mat-checkbox>
</mat-list-item>
</mat-list>
</mat-card-content>
</mat-card>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<mat-card>
<mat-card-subtitle>
Studies:
</mat-card-subtitle>
<mat-card-content>
<mat-list>
<mat-list-item *ngFor="let study of availableStudyList">
<mat-checkbox [checked]="studyStatusDictionary.get(study).checked"
[disabled]="studyStatusDictionary.get(study).disabled || this.getTotalFitCount(this.filteredDataSource,
this.adminViewEnabled, undefined, study, undefined) == 0"
(change)="studyCheckboxChange(study, $event.checked)">
<span
style="font-weight: 600 !important;">{{study}}</span>&nbsp;<i style="font-weight: 400 !important;">({{this.getTotalFitCount(this.filteredDataSource,
this.adminViewEnabled, undefined, study, undefined)}} fits)</i>
</mat-checkbox>
</mat-list-item>
</mat-list>
</mat-card-content>
</mat-card>
</div>
</div>
<div class="row" *ngIf="this.adminViewEnabled">
<div class="col-xs-12">
<mat-card>
<mat-card-subtitle>
Tags
</mat-card-subtitle>
<mat-card-content>
<mat-list>
<mat-list-item *ngFor="let tag of uniqueFitTags | async">
<mat-checkbox [checked]="tagStatusDictionary.get(tag).checked"
[disabled]="tagStatusDictionary.get(tag).disabled || this.getTotalFitCount(this.filteredDataSource,
this.adminViewEnabled, undefined, undefined, tag) == 0"
(change)="tagCheckboxChange(tag, $event.checked)">
<span
style="font-weight: 600 !important;">{{tag}}</span>&nbsp;<i style="font-weight: 400 !important;">({{ tagStatusDictionary.get(tag).disabled ? "refresh to use" : this.getTotalFitCount(this.filteredDataSource,
this.adminViewEnabled, undefined, undefined, tag) + " total fits"}})</i>
</mat-checkbox>
</mat-list-item>
</mat-list>
</mat-card-content>
</mat-card>
</div>
</div>
</div>
import { Component, OnInit, Input, SimpleChanges, OnChanges, ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, Output, EventEmitter, ChangeDetectorRef } from '@angular/core';
import { ComplexesSpeciesEntry, ComplexesSpeciesExperimentTree, ComplexesSpeciesStudyTree, ComplexesSpeciesTree, FitTableInformation, PairWrapper, StudyNameSet } from 'src/app/models/postproc-component-classes';
import { SpeciesService } from 'src/app/services/species.service';
import { Observable, of } from 'rxjs';
import { Router } from '@angular/router';
import { FitDetailsService } from 'src/app/services/fit-details.service';
export class FilterCheckboxStatus {
checked: boolean;
disabled: boolean;
}
// TODO: Left off - still many bugs with the checking, but create change event for experiment and check change ref for nonexperiment
@Component({
selector: 'app-factor-table-filter',
templateUrl: './factor-table-filter.component.html',
styleUrls: ['./factor-table-filter.component.css']
})
export class FactorTableFilterComponent implements OnInit, OnChanges {
@Input("fullComplexSpeciesTree") $complexSpeciesTree: Observable<ComplexesSpeciesTree>;
@Input("filteredDataSource") filteredDataSource: [ComplexesSpeciesEntry, ComplexesSpeciesStudyTree][];
@Input("complexAndExperimentFilteredDataSource") complexAndExperimentFilteredDataSource: [ComplexesSpeciesEntry, ComplexesSpeciesStudyTree][];
@Input("nonExperimentFilteredDataSource") nonExperimentFilteredDataSource: [ComplexesSpeciesEntry, ComplexesSpeciesStudyTree][];
@Input('adminViewEnabled') adminViewEnabled: boolean = false;
@Input('uniqueFitTags') uniqueFitTags: Observable<String[]> = of([]);
@Output("selectedStudiesAndSpeciesListChange") selectedStudiesAndSpeciesListChange: EventEmitter<{ selectedStudies: string[], selectedSpecies: number[] }> = new EventEmitter<{ selectedStudies: string[], selectedSpecies: number[] }>();
@Output("selectedExperimentListChange") selectedExperimentListChange: EventEmitter<string[]> = new EventEmitter<string[]>();
@Output("selectedTagListChange") selectedTagListChange: EventEmitter<string[]> = new EventEmitter<string[]>();
public studyStatusDictionary: Map<string, FilterCheckboxStatus> = new Map<string, FilterCheckboxStatus>();
public speciesStatusDictionary: Map<number, FilterCheckboxStatus> = new Map<number, FilterCheckboxStatus>();
public experimentStatusDictionary: Map<string, FilterCheckboxStatus> = new Map<string, FilterCheckboxStatus>();
public tagStatusDictionary: Map<string, FilterCheckboxStatus> = new Map<string, FilterCheckboxStatus>();
public availableStudyList: string[];
public availableSpeciesList: number[];
public availableExperimentList: string[];
public selectedStudyList: string[] = [];
public selectedSpeciesList: number[] = [];
public selectedExperimentList: string[] = [];
public selectedTagList: string[] = [];
public nonDisabledExperimentListCount: number;
public numberOfFits: number;
constructor(public speciesService: SpeciesService,
public fitDetailsService: FitDetailsService,
private changeDetectorRef: ChangeDetectorRef,
private router: Router) { }
ngOnInit(): void {
this.$complexSpeciesTree.subscribe(complexSpeciesTree => {
let availableStudiesAndSpecies: [string[], number[], string[]] = this.getAvailableStudiesSpeciesExperimentsFromComplexSpeciesTree(complexSpeciesTree);
this.availableStudyList = availableStudiesAndSpecies[0];
this.availableSpeciesList = availableStudiesAndSpecies[1];
this.availableExperimentList = availableStudiesAndSpecies[2];
this.availableStudyList.forEach(study => {
if (!this.studyStatusDictionary.has(study)) {
this.studyStatusDictionary.set(study, { checked: false, disabled: false });
}
});
this.availableSpeciesList.forEach(species => {
if (!this.speciesStatusDictionary.has(species)) {
this.speciesStatusDictionary.set(species, { checked: false, disabled: false });
}
});
this.availableExperimentList.forEach(experiment => {
if (!this.experimentStatusDictionary.has(experiment)) {
this.experimentStatusDictionary.set(experiment, { checked: false, disabled: false });
}
})
this.nonDisabledExperimentListCount = Array.from(this.experimentStatusDictionary.values()).filter(status => {
return status.disabled == false;
}).length;
});
}
ngOnChanges(changes: SimpleChanges) {
if (changes.nonExperimentFilteredDataSource != undefined && changes.nonExperimentFilteredDataSource.currentValue != changes.nonExperimentFilteredDataSource.previousValue) {
this.handleNonExperimentFilterDataSourceChange(this.nonExperimentFilteredDataSource);
}
if (changes.complexAndExperimentFilteredDataSource != undefined && changes.complexAndExperimentFilteredDataSource.currentValue != changes.complexAndExperimentFilteredDataSource.previousValue) {
this.handleComplexAndExperimentFilterDataSourceChange(changes.complexAndExperimentFilteredDataSource.currentValue);
}
if (changes.filteredDataSource != undefined && changes.filteredDataSource.currentValue != changes.filteredDataSource.previousValue) {
this.numberOfFits = this.getTotalFitCount(changes.filteredDataSource.currentValue, this.adminViewEnabled);
}
if (changes.uniqueFitTags != undefined && changes.uniqueFitTags.currentValue != changes.uniqueFitTags.previousValue) {
console.log();
this.uniqueFitTags.subscribe(updatedTags => {
updatedTags.forEach(updatedTag => {
// If there is a new tag that isnt in the directionary
if (!this.tagStatusDictionary.has(updatedTag as string)) {
this.tagStatusDictionary.set(updatedTag as string, { checked: false, disabled: changes.uniqueFitTags.previousValue != undefined });
}
})
// If the dictionary has a tag that isn't provided.
this.tagStatusDictionary.forEach((value: FilterCheckboxStatus, originalTag: string) => {
if (!updatedTags.includes(originalTag)) {
// uncheck tag from filter
if (this.tagStatusDictionary.get(originalTag).checked == true) {
this.tagCheckboxChange(originalTag, false);
}
// delete tag from list
this.tagStatusDictionary.delete(originalTag);
}
});
})
this.changeDetectorRef.detectChanges();
}
}
/*
* TODO: LEFT OFF at retrieving experiments from methods. Fix the variable type of the method calls. And use the experiment list from the full to store at availableExperiments. Then get the different of availableExperiments and the nonExperimentFilteredDataSource experimentList to curate the selectedExperiments. Disabling logic is unnecessary since disabled experiments will not be displayed (perhaps an alternative is use an *ngIf to remove disabled experiments rather than curating the selectedExperimentList).
*/
public handleNonExperimentFilterDataSourceChange(nonExperimentFilteredDataSource: [ComplexesSpeciesEntry, ComplexesSpeciesStudyTree][]) {
let nonExperimentFilteredExperiments: string[] = this.getAvailableExperimentFromDatasource(nonExperimentFilteredDataSource);
this.availableExperimentList.forEach(availableExperiment => {
var nonFilteredExperimentStatus: FilterCheckboxStatus = this.experimentStatusDictionary.get(availableExperiment);
nonFilteredExperimentStatus.disabled = !nonExperimentFilteredExperiments.includes(availableExperiment);
this.experimentStatusDictionary.set(availableExperiment, nonFilteredExperimentStatus);
});
this.nonDisabledExperimentListCount = Array.from(this.experimentStatusDictionary.values()).filter(status => {
return status.disabled == false;
}).length;
}
public getAvailableExperimentFromDatasource(datasource: [ComplexesSpeciesEntry, ComplexesSpeciesStudyTree][]): string[] {
if (datasource == undefined || datasource == null || datasource == []) {
return [];
} else {
var uniqueExperiments: string[] = [];
datasource.forEach(complexSpeciesEntry => {
Object.values(complexSpeciesEntry[1].complexesSpeciesStudyTree).forEach(complexSpeciesStudyEntry => {
Object.values(complexSpeciesStudyEntry.right.complexesSpeciesExperimentTree).forEach(complexSpeciesExperimentEntry => {
complexSpeciesExperimentEntry.left.experimentNameSet.forEach(experimentName => {
if (!uniqueExperiments.includes(experimentName)) {
uniqueExperiments.push(experimentName);
}
});
});
});
});
return uniqueExperiments;
}
}
// private handleFilterDataSourceChange(newFilterDataSource: [ComplexesSpeciesEntry, ComplexesSpeciesStudyTree][]): void {
// let availableStudiesAndSpecies: [string[], number[]] = this.getAvailableStudiesAndSpecies(newFilterDataSource);
// this.availableStudyList = availableStudiesAndSpecies[0];
// this.availableSpeciesList = availableStudiesAndSpecies[1];
// let updatedSelectedStudyList: string[] = this.availableStudyList.filter(study => {
// if (this.studyStatusDictionary.get(study)) {
// return true;
// } else {
// return false;
// }
// });
// let updatedSelectedSpeciesList: number[] = this.availableSpeciesList.filter(species => {
// if (this.speciesStatusDictionary.get(species)) {
// return true;
// } else {
// return false;
// }
// });
// /*
// * If there is any changed in the selected study list or selected species list
// */
// if (updatedSelectedStudyList != this.selectedStudyList || updatedSelectedSpeciesList != this.selectedSpeciesList) {
// this.selectedStudyList = updatedSelectedStudyList;
// this.selectedSpeciesList = updatedSelectedSpeciesList;
// this.selectedStudiesAndSpeciesListChange.emit({ selectedStudies: this.selectedStudyList, selectedSpecies: this.selectedSpeciesList });
// }
// }
private handleComplexAndExperimentFilterDataSourceChange(newComplexFilterDataSource: [ComplexesSpeciesEntry, ComplexesSpeciesStudyTree][]): void {
let complexFilteredStudiesAndSpecies: [string[], number[], string[]] = this.getAvailableStudiesSpeciesExperimentsFromDatasource(newComplexFilterDataSource);
let complexFilteredStudyList: string[] = complexFilteredStudiesAndSpecies[0];
let complexFilteredSpeciesList: number[] = complexFilteredStudiesAndSpecies[1];
this.availableStudyList.forEach(availableStudy => {
var availableStudyStatus: FilterCheckboxStatus = this.studyStatusDictionary.get(availableStudy);
availableStudyStatus.disabled = !complexFilteredStudyList.includes(availableStudy);
this.studyStatusDictionary.set(availableStudy, availableStudyStatus);
});
this.availableSpeciesList.forEach(availableSpecies => {
var availableSpeciesStatus: FilterCheckboxStatus = this.speciesStatusDictionary.get(availableSpecies);
availableSpeciesStatus.disabled = !complexFilteredSpeciesList.includes(availableSpecies);
this.speciesStatusDictionary.set(availableSpecies, availableSpeciesStatus);
});
}
private getAvailableStudiesSpeciesExperimentsFromComplexSpeciesTree(complexSpeciesTree: ComplexesSpeciesTree): [string[], number[], string[]] {
var uniqueSpecies: number[] = [];
var uniqueStudies: string[] = [];
var uniqueExperiments: string[] = [];
Object.values(complexSpeciesTree.complexesSpeciesTree).forEach(complexSpeciesEntry => {
complexSpeciesEntry.left.taxIdList.forEach(taxId => {
if (!uniqueSpecies.includes(taxId)) {
uniqueSpecies.push(taxId);
}
});
Object.values(complexSpeciesEntry.right.complexesSpeciesStudyTree).forEach(complexSpeciesStudyEntry => {
complexSpeciesStudyEntry.left.studyNameSet.forEach(study => {
if (!uniqueStudies.includes(study)) {
uniqueStudies.push(study);
}
Object.values(complexSpeciesStudyEntry.right.complexesSpeciesExperimentTree).forEach(complexSpeciesExperimentEntry => {
complexSpeciesExperimentEntry.left.experimentNameSet.forEach(experimentName => {
if (!uniqueExperiments.includes(experimentName)) {
uniqueExperiments.push(experimentName);
}
});
})
});
});
});
return [uniqueStudies, uniqueSpecies, uniqueExperiments];
}
private getAvailableStudiesSpeciesExperimentsFromDatasource(datasource: [ComplexesSpeciesEntry, ComplexesSpeciesStudyTree][]): [string[], number[], string[]] {
if (datasource == undefined || datasource == null || datasource == []) {
return [[], [], []];
} else {
var uniqueSpecies: number[] = [];
var uniqueStudies: string[] = [];
var uniqueExperiments: string[] = [];
datasource.forEach(complexSpeciesEntry => {
complexSpeciesEntry[0].taxIdList.forEach(taxId => {
if (!uniqueSpecies.includes(taxId)) {
uniqueSpecies.push(taxId);
}
});
Object.values(complexSpeciesEntry[1].complexesSpeciesStudyTree).map(complexSpeciesStudyEntry => {
complexSpeciesStudyEntry.left.studyNameSet.forEach(study => {
if (!uniqueStudies.includes(study)) {
uniqueStudies.push(study);
}
});
Object.values(complexSpeciesStudyEntry.right.complexesSpeciesExperimentTree).forEach(complexSpeciesExperimentEntry => {
complexSpeciesExperimentEntry.left.experimentNameSet.forEach(experimentName => {
if (!uniqueExperiments.includes(experimentName)) {
uniqueExperiments.push(experimentName);
}
});
});
});
});
return [uniqueStudies, uniqueSpecies, uniqueExperiments];
}
}
public tagCheckboxChange(tag: string, checked: boolean): void {
var availableTagStatus: FilterCheckboxStatus = this.tagStatusDictionary.get(tag);
availableTagStatus.checked = checked;
this.tagStatusDictionary.set(tag, availableTagStatus);
if (checked == false) {
this.selectedTagList.splice(this.selectedTagList.indexOf(tag), 1);
} else if (checked == true) {
this.selectedTagList.push(tag);
}
// TODO: Cloning array to change reference to initiate ngOnChanges
this.selectedTagListChange.emit([...this.selectedTagList]);
}
public speciesCheckboxChange(species: number, checked: boolean): void {
var availableSpeciesStatus: FilterCheckboxStatus = this.speciesStatusDictionary.get(species);
availableSpeciesStatus.checked = checked;
this.speciesStatusDictionary.set(species, availableSpeciesStatus);
if (checked == false) {
this.selectedSpeciesList.splice(this.selectedSpeciesList.indexOf(species), 1);
} else if (checked == true) {
this.selectedSpeciesList.push(species);
}
this.selectedStudiesAndSpeciesListChange.emit({ selectedStudies: this.selectedStudyList, selectedSpecies: this.selectedSpeciesList });
}
public studyCheckboxChange(study: string, checked: boolean): void {
var availableStudyStatus: FilterCheckboxStatus = this.studyStatusDictionary.get(study);
availableStudyStatus.checked = checked;
this.studyStatusDictionary.set(study, availableStudyStatus);
if (checked == false) {
this.selectedStudyList.splice(this.selectedStudyList.indexOf(study), 1);
} else if (checked == true) {
this.selectedStudyList.push(study);
}
this.selectedStudiesAndSpeciesListChange.emit({ selectedStudies: this.selectedStudyList, selectedSpecies: this.selectedSpeciesList });
}
public experimentCheckboxChange(experiment: string, checked: boolean) {
var availableExperimentStatus: FilterCheckboxStatus = this.experimentStatusDictionary.get(experiment);
availableExperimentStatus.checked = checked;
this.experimentStatusDictionary.set(experiment, availableExperimentStatus);
if (checked == false) {
this.selectedExperimentList.splice(this.selectedExperimentList.indexOf(experiment), 1);
} else if (checked == true) {
this.selectedExperimentList.push(experiment);
}
// TODO: Cloning array to change reference to initiate ngOnChanges
this.selectedExperimentListChange.emit([...this.selectedExperimentList]);
}
public getTotalFitCount(dataSource: [ComplexesSpeciesEntry, ComplexesSpeciesStudyTree][], includeNonCurated: boolean, taxIdFilter?: number, studyNameFilter?: string, tagFilter?: string): number {
return dataSource.reduce((currentCount, dataSourceEntry) => {
let nextCount: number = 0;
if ((taxIdFilter != undefined && dataSourceEntry[0].taxIdList.includes(taxIdFilter)) || taxIdFilter == undefined) {
nextCount = this.getComplexFitCount(dataSourceEntry[1].complexesSpeciesStudyTree, includeNonCurated, studyNameFilter, tagFilter);
}
return currentCount + nextCount;
}, 0);
}
/**
* Colpied from factor table component TODO: modularize common functions
*/
public getComplexFitCount(complexesSpeciesStudyTree: { [index: string]: PairWrapper<StudyNameSet, ComplexesSpeciesExperimentTree> }, includeNonCurated: boolean, studyNameFilter?: string, tagFilter?: string): number {
var studyFitsCount: number = 0;
Object.keys(complexesSpeciesStudyTree).forEach((studyNameSetIndex: string) => {
if ((studyNameFilter != undefined && complexesSpeciesStudyTree[studyNameSetIndex].left.studyNameSet.includes(studyNameFilter)) || studyNameFilter == undefined) {
const experimentTree: ComplexesSpeciesExperimentTree = complexesSpeciesStudyTree[studyNameSetIndex].right;
Object.keys(experimentTree.complexesSpeciesExperimentTree).forEach((experimentNameSetIndex: string) => {
var fits: FitTableInformation[] = experimentTree.complexesSpeciesExperimentTree[experimentNameSetIndex].right;
if (tagFilter != undefined) {
// NOTE 3/15/21: Added regex to remove quotes added to strings with spaces
fits = fits.filter(fit => fit.tagList.map(tag => tag.replace(/['"]+/g, '')).includes(tagFilter));
}
let fitLength = fits != undefined ? fits.filter(fit => includeNonCurated ? true : this.getFitCurationStatus(fit)).length : 0;
studyFitsCount = studyFitsCount + fitLength;
});
}
});
return studyFitsCount;
}
/**
* Colpied from factor table component TODO: modularize common functions
*/
private getFitCurationStatus(fit: FitTableInformation): boolean {
return (fit.curated == null || fit.curated == undefined) ? fit.pipelineCurated : fit.curated;
//return !(fit.curated == null || fit.curated == undefined) && fit.curated;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment