src/app/components/report/report.component.ts
OnInit
selector | app-report |
styleUrls | ./report.component.scss |
templateUrl | ./report.component.html |
Properties |
Methods |
Inputs |
Outputs |
constructor(reportService: ReportService, ts: TreeService, ga: GoogleAnalyticsService)
|
||||||||||||
Parameters :
|
asFullData | |
Type : Row[]
|
|
Default value : []
|
|
bmType | |
Type : string
|
|
Default value : ''
|
|
compareData | |
Type : Observable<literal type>
|
|
compareSheets | |
Type : CompareData[]
|
|
Default value : []
|
|
currentSheet | |
Type : Sheet
|
|
currentSheetConfig | |
Type : Observable<SheetConfig>
|
|
fullDataByOrgan | |
Type : Row[][]
|
|
Default value : []
|
|
hideReportCompareTab | |
Type : boolean
|
|
Default value : false
|
|
inputReportData | |
Type : Observable<Report>
|
|
linksData$ | |
Type : Observable<LinksASCTBData>
|
|
sheetData | |
Type : Row[]
|
|
Default value : []
|
|
closeReport | |
Type : EventEmitter
|
|
computedReport | |
Type : EventEmitter
|
|
deleteSheet | |
Type : EventEmitter
|
|
customColors | ||||||
customColors(v: string)
|
||||||
Parameters :
Returns :
string
|
deleteCompareSheetReport | ||||||
deleteCompareSheetReport(i: number)
|
||||||
Parameters :
Returns :
void
|
downloadCompareSheetReport | ||||||
downloadCompareSheetReport(i: number)
|
||||||
Parameters :
Returns :
{ sheet: any; sheetName: any; name: string; }
|
downloadData |
downloadData()
|
Returns :
{ sheet: any; sheetName: any; name: string; }
|
downloadReport | ||||||
downloadReport(i)
|
||||||
Parameters :
Returns :
void
|
downloadReportByOrgan |
downloadReportByOrgan()
|
Returns :
void
|
getBiomarkerLabel | ||||||
getBiomarkerLabel(bmType: string)
|
||||||
Parameters :
|
getBMCTAS | |||||||||
getBMCTAS(bmCtPairs: BmCtPairings[], download: Record
|
|||||||||
Parameters :
Returns :
{}
|
getTotals | |||||||||
getTotals(data: CByOrgan[], key: string)
|
|||||||||
Parameters :
Returns :
any
|
makeOntologyLinksGraphData |
makeOntologyLinksGraphData(reportData: Report, sheetData: Row[])
|
Returns :
{}
|
biomarkersCounts |
Type : BiomarkersCounts[]
|
Default value : []
|
biomarkersSeperateNames |
Type : BiomarkersNamesInReport[]
|
Default value : []
|
clickButton |
Default value : false
|
compareReport |
Type : CompareReport[]
|
Default value : []
|
countsByOrgan |
Type : MatTableDataSource<CByOrgan>
|
Default value : new MatTableDataSource()
|
Public ga |
Type : GoogleAnalyticsService
|
ontologyLinkGraphData |
Type : ReturnType<>
|
Default value : []
|
reportData |
Type : Report
|
Default value : {
anatomicalStructures: [],
cellTypes: [],
biomarkers: [],
ASWithNoLink: [],
CTWithNoLink: [],
BWithNoLink: [],
ASWithNoCT: [],
CTWithNoB: [],
}
|
Public reportService |
Type : ReportService
|
resultDataByOrganName |
Type : Report
|
Default value : {
anatomicalStructures: [],
ASWithNoLink: [],
CTWithNoLink: [],
BWithNoLink: [],
cellTypes: [],
biomarkers: [],
ASWithNoCT: [],
CTWithNoB: [],
}
|
SheetConfig |
Type : SheetConfig
|
sort |
Type : MatSort
|
Decorators :
@ViewChild(MatSort)
|
total_AS_AS |
Type : number
|
Default value : 0
|
total_AS_CT |
Type : number
|
Default value : 0
|
total_CT_B |
Type : number
|
Default value : 0
|
Public ts |
Type : TreeService
|
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { BiomarkersCounts, BiomarkersNamesInReport, CByOrgan, Report } from '../../models/report.model';
import { CompareData, CompareReport, Row, Sheet, SheetConfig } from '../../models/sheet.model';
import { ReportService } from './report.service';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import * as moment from 'moment';
import { GoogleAnalyticsService } from 'ngx-google-analytics';
import { Observable } from 'rxjs';
import * as XLSX from 'xlsx';
import { GaAction, GaCategory } from '../../models/ga.model';
import { BmCtPairings, LinksASCTBData } from '../../models/tree.model';
import { TreeService } from '../../modules/tree/tree.service';
@Component({
selector: 'app-report',
templateUrl: './report.component.html',
styleUrls: ['./report.component.scss'],
})
export class ReportComponent implements OnInit {
reportData: Report = {
anatomicalStructures: [],
cellTypes: [],
biomarkers: [],
ASWithNoLink: [],
CTWithNoLink: [],
BWithNoLink: [],
ASWithNoCT: [],
CTWithNoB: [],
};
resultDataByOrganName: Report = {
anatomicalStructures: [],
ASWithNoLink: [],
CTWithNoLink: [],
BWithNoLink: [],
cellTypes: [],
biomarkers: [],
ASWithNoCT: [],
CTWithNoB: [],
};
countsByOrgan: MatTableDataSource<CByOrgan> = new MatTableDataSource();
displayedColumns: string[] = [
'organName',
'ASWithNoLink',
'CTWithNoLink',
'BWithNoLink',
'ASWithNoCT',
'CTWithNoB',
'AS_AS',
'AS_CT',
'CT_BM',
'anatomicalStructures',
'cellTypes',
];
biomarkersSeperateNames: BiomarkersNamesInReport[] = [];
compareReport: CompareReport[] = [];
clickButton = false; // for mat expansion panel download button
@ViewChild(MatSort) sort!: MatSort;
ontologyLinkGraphData: ReturnType<typeof this.makeOntologyLinksGraphData> = [];
SheetConfig!: SheetConfig;
total_AS_AS = 0;
biomarkersCounts: BiomarkersCounts[] = [];
@Input() compareSheets: CompareData[] = [];
@Input() sheetData: Row[] = [];
@Input() asFullData: Row[] = [];
@Input() fullDataByOrgan: Row[][] = [];
@Input() currentSheet!: Sheet;
@Input() linksData$!: Observable<LinksASCTBData>;
@Input() inputReportData!: Observable<Report>;
@Input() currentSheetConfig!: Observable<SheetConfig>;
@Input() compareData!: Observable<{ data: Row[]; sheets: CompareData[] }>;
@Input() bmType = '';
@Input() hideReportCompareTab = false;
@Output() closeReport = new EventEmitter<void>();
@Output() computedReport = new EventEmitter<Report>();
@Output() deleteSheet = new EventEmitter<number>();
total_AS_CT = 0;
total_CT_B = 0;
constructor(
public reportService: ReportService,
public ts: TreeService,
public ga: GoogleAnalyticsService,
) {}
ngOnInit(): void {
this.reportService.reportData$.subscribe((data) => {
this.reportData = data.data as Report;
this.computedReport.emit(data.data as Report);
this.ontologyLinkGraphData = this.makeOntologyLinksGraphData(data.data as Report, this.sheetData);
});
this.linksData$.subscribe((data) => {
this.total_AS_AS = data.AS_AS;
this.total_AS_CT = data.AS_CT;
this.total_CT_B = data.CT_B;
});
this.reportService.compareData$.subscribe((data) => {
this.compareReport = data.data;
});
this.currentSheetConfig.subscribe((data) => {
this.SheetConfig = data;
});
this.compareData.subscribe((data) => {
if (data.sheets.length && data.data.length) {
this.reportService.makeCompareData(this.reportData, data.data, data.sheets);
}
});
this.fullDataByOrgan.forEach((data) => {
this.ts.makeTreeData(this.currentSheet, data, this.compareData, true);
});
this.reportService.makeReportData(this.currentSheet, this.sheetData, this.bmType, true);
}
makeOntologyLinksGraphData(reportData: Report, sheetData: Row[]) {
const { result, biomarkersSeperateNames } = this.reportService.makeAllOrganReportDataByOrgan(
sheetData,
this.asFullData,
);
const biomarkerCols: string[] = [];
biomarkersSeperateNames.forEach((bm) => {
if (this.displayedColumns.includes(bm.name) === false) {
biomarkerCols.push(bm.name);
}
this.displayedColumns = [
'organName',
'tableVersion',
'anatomicalStructures',
'cellTypes',
'b_total',
...biomarkerCols,
'ASWithNoLink',
'CTWithNoLink',
'BWithNoLink',
'ASWithNoCT',
'CTWithNoB',
'AS_AS',
'AS_CT',
'CT_BM',
];
});
const tableVersions = new Map<string, string>();
sheetData.forEach((element) => {
if (!(element.organName in tableVersions.keys())) {
tableVersions.set(element.organName, element.tableVersion);
}
});
this.biomarkersSeperateNames = biomarkersSeperateNames;
this.linksData$.subscribe((data) => {
this.countsByOrgan = new MatTableDataSource(
this.reportService
.makeAllOrganReportDataCountsByOrgan(result, data, tableVersions)
.sort((a, b) => a.organName?.localeCompare(b.organName ?? '') ?? 0),
);
this.biomarkersCounts = [];
this.biomarkersSeperateNames.forEach((bm) => {
this.biomarkersCounts.push({
name: bm.name,
value: this.countsByOrgan.data[0][bm.name as keyof CByOrgan] as number,
});
});
this.countsByOrgan.sort = this.sort;
});
return [
{
results: [
{
name: 'with Uberon Links',
value: reportData.anatomicalStructures.length - reportData.ASWithNoLink.length,
},
{
name: 'without Uberon Links',
value: reportData.ASWithNoLink.length,
},
],
label: 'Total Anatomical Structures',
},
{
results: [
{
name: 'with CL Links',
value: reportData.cellTypes.length - reportData.CTWithNoLink.length,
},
{ name: 'without CL Links', value: reportData.CTWithNoLink.length },
],
label: 'Total Cell Types',
},
{
results: [
{
name: 'with HGNC Links',
value: reportData.biomarkers.length - reportData.BWithNoLink.length,
},
{ name: 'without HGNC Links', value: reportData.BWithNoLink.length },
],
label: this.getBiomarkerLabel(this.bmType),
},
];
}
getBiomarkerLabel(bmType: string) {
return bmType === 'Gene'
? 'Total Gene Biomarkers'
: bmType === 'Protein'
? 'Total Protein Biomarkers'
: bmType === 'Lipids'
? 'Total Lipids Biomarkers'
: bmType === 'Metabolites'
? 'Total Metabolites Biomarkers'
: bmType === 'Proteoforms'
? 'Total Proteoforms Biomarkers'
: 'Total Biomarkers';
}
customColors(v: string) {
const mapper = {
'with Uberon Links': '#E41A1C',
'without Uberon Links': '#f5bcba',
'with CL Links': '#377EB8',
'without CL Links': '#abc9eb',
'with HGNC Links': '#4DAF4A',
'without HGNC Links': '#bce8be',
};
return mapper[v as keyof typeof mapper];
}
deleteCompareSheetReport(i: number) {
this.clickButton = true;
this.compareReport.splice(i, 1);
this.deleteSheet.emit(i);
this.ga.event(GaAction.CLICK, GaCategory.REPORT, 'Delete a sheet comparison', i);
}
getTotals(data: CByOrgan[], key: string) {
return data
.map((t) => (t[key as keyof CByOrgan] ? (t[key as keyof CByOrgan] as number) : 0))
.reduce((acc, value) => acc + value, 0);
}
downloadData() {
const download: Record<string, string>[] = [];
const totalRows = 6;
for (
let i = 0;
i <
Math.max(
this.reportData.anatomicalStructures.length,
this.reportData.cellTypes.length,
this.reportData.biomarkers.length,
);
i++
) {
const row: Record<string, string> = {};
if (i < this.reportData.anatomicalStructures.length) {
row['Unique Anatomical Structures'] = this.reportData.anatomicalStructures[i].structure;
if (!this.reportData.anatomicalStructures[i].uberon.includes('UBERON')) {
row['AS with no Uberon Link'] = this.reportData.anatomicalStructures[i].structure;
}
}
if (i < this.reportData.cellTypes.length) {
row['Unique Cell Types'] = this.reportData.cellTypes[i].structure;
row['CT ID'] = this.reportData.cellTypes[i].link;
if (!this.reportData.cellTypes[i].link.includes('CL')) {
row['CL with no Link'] = this.reportData.cellTypes[i].structure;
}
}
if (i < this.reportData.biomarkers.length) {
row['Unique Biomarkers'] = this.reportData.biomarkers[i].structure;
row['BM ID'] = this.reportData.biomarkers[i].link;
}
if (i < this.reportData.BWithNoLink.length) {
row['Biomarkers with no links'] = this.reportData.BWithNoLink[i].structure;
}
download.push(row);
}
const sheetWS = XLSX.utils.json_to_sheet(download);
sheetWS['!cols'] = [];
for (let i = 0; i < totalRows; i++) {
sheetWS['!cols'].push({ wch: 30 });
}
const dt = moment(new Date()).format('YYYY.MM.DD_hh.mm');
const sn = this.currentSheet.display.toLowerCase().replace(' ', '_');
return {
sheet: sheetWS,
sheetName: this.currentSheet.display,
name: `ASCT+B-Reporter_${sn}_${dt}_Report.xlsx`,
};
}
downloadReport(i = -1) {
const wb = XLSX.utils.book_new();
const allReport = [];
/**
* When all reports need to be downloaded
*/
if (i === -1) {
allReport.push(this.downloadData());
this.ga.event(GaAction.CLICK, GaCategory.REPORT, 'Download Full Report');
if (this.compareReport) {
for (const [sheet, _unused] of this.compareReport.entries()) {
allReport.push(this.downloadCompareSheetReport(sheet));
}
}
} else {
/**
* When a single compare sheet report needs to be downloaded
* Not firing a Google Analytics event in this case since the downloadCompareSheetReport() method already does so.
*/
allReport.push(this.downloadCompareSheetReport(i));
}
for (const book of allReport) {
XLSX.utils.book_append_sheet(wb, book.sheet, book.sheetName);
}
XLSX.writeFile(wb, allReport[0].name);
}
downloadReportByOrgan() {
const sheetName = 'countByOrgan';
const fileName = 'countsByOrgans';
const targetTableElm = document.getElementById('countsByOrgans');
const allReport: ReturnType<typeof this.downloadData>[] = [];
const organsList: string[] = [];
const wb = XLSX.utils.table_to_book(targetTableElm, {
sheet: sheetName,
} as XLSX.Table2SheetOpts);
this.fullDataByOrgan.forEach((data) => {
organsList.push(data[0].organName);
this.reportService.makeReportData(this.currentSheet, data, this.bmType);
allReport.push(this.downloadData());
});
this.reportService.makeReportData(this.currentSheet, this.sheetData, this.bmType);
let i = 0;
for (const book of allReport) {
XLSX.utils.book_append_sheet(wb, book.sheet, organsList[i]);
i += 1;
}
XLSX.writeFile(wb, `${fileName}.xlsx`);
}
downloadCompareSheetReport(i: number) {
this.clickButton = true;
const totalRows = 6;
const sheet = this.compareReport[i];
const keyMapper = {
identicalAS: 'Identical Anatomical Structures',
newAS: 'New Anatomical Structres',
identicalCT: 'Identical Cell Types',
newCT: 'New Cell Types',
identicalB: 'Identical Biomarkers',
newB: 'New Biomarkers',
};
let download: Record<string, string>[] = [];
const keys = Object.keys(this.compareReport[i]);
for (const key of keys) {
if (typeof sheet[key as keyof CompareReport] === 'object' && key in keyMapper) {
for (const [idx, value] of (sheet[key as keyof CompareReport] as string[]).entries()) {
const t: Record<string, string> = {};
t[keyMapper[key as keyof typeof keyMapper]] = value;
if (download[idx]) {
download[idx] = { ...download[idx], ...t };
} else {
download.push(t);
}
}
}
}
download = this.getBMCTAS(sheet.identicalBMCTPair as unknown as BmCtPairings[], download);
const sheetWS = XLSX.utils.json_to_sheet(download, {
header: Object.values(keyMapper),
});
sheetWS['!cols'] = [];
for (let j = 0; j < totalRows; j++) {
sheetWS['!cols'].push({ wch: 30 });
}
const dt = moment(new Date()).format('YYYY.MM.DD_hh.mm');
const sn = sheet.title.toLowerCase().replace(' ', '_');
this.ga.event(GaAction.CLICK, GaCategory.REPORT, 'Compare sheet download', sn as never);
return {
sheet: sheetWS,
sheetName: sheet.title,
name: `ASCT+B-Reporter_Derived_${sn}_${dt}_Report.xlsx`,
};
}
getBMCTAS(bmCtPairs: BmCtPairings[], download: Record<string, string>[]) {
const AS_CT_BM: Set<string> = new Set<string>();
let index = 0;
bmCtPairs.forEach((pair) => {
const newPair = `${pair.AS_NAME} (${pair.AS_ID}), ${pair.CT_NAME} (${pair.CT_ID}), ${pair.BM_NAME}(${pair.BM_ID})`;
if (!AS_CT_BM.has(newPair)) {
AS_CT_BM.add(newPair);
const t = {
'': '',
AS: `${pair.AS_NAME} (${pair.AS_ID})`,
CT: `${pair.CT_NAME} (${pair.CT_ID})`,
Biomarker: `${pair.BM_NAME} (${pair.BM_ID})`,
};
if (download[index]) {
download[index] = { ...download[index], ...t };
} else {
download.push(t);
}
index += 1;
}
});
return download;
}
}
<app-sidenav>
<div header>
<app-sidenav-header
[title]="'Report for ' + currentSheet.display"
[download]="true"
(downloadFn)="downloadReport()"
[tooltipString]="'Various statistics calculated from the data'"
(closeSideNav)="closeReport.emit()"
>
</app-sidenav-header>
</div>
<div body class="report">
<mat-tab-group mat-stretch-tabs class="tabs px-4 mt-4">
<mat-tab [label]="'Main Sheet'">
<div class="content">
<br />
<div class="h5">Overview</div>
<br />
<div class="overview">
<p class="text-muted mb-3">
<small class="fw-bolder">UNIQUE ENTITIES</small>
</p>
<div class="d-flex justify-content-between align-content-center pr-5 h6">
<span class="fw-normal">Anatomical structures</span>
<span class="number fw-bolder numerical-padding">{{ reportData.anatomicalStructures.length }}</span>
</div>
<hr />
<div class="d-flex justify-content-between align-content-center pr-5 h6">
<span class="fw-normal">Cell types </span>
<span class="number fw-bolder">{{ reportData.cellTypes.length }}</span>
</div>
<hr />
<div class="d-flex justify-content-between align-content-center pr-5 h6">
<span class="fw-normal">Biomarkers </span>
<span class="number fw-bolder">{{ reportData.biomarkers.length }}</span>
</div>
<hr />
<br />
<p class="text-muted mb-3">
<small class="fw-bolder">ENTITY LINKS</small>
</p>
<div class="d-flex justify-content-between align-content-center pr-5 h6">
<span class="fw-normal"
>Anatomical Structures <span class="badge bg-danger">part_of</span> AS links
</span>
<span class="number fw-bolder">{{ total_AS_AS }}</span>
</div>
<hr />
<div class="d-flex justify-content-between align-content-center pr-5 h6">
<span class="fw-normal">Cell Types <span class="badge badge-primary">located_in</span> AS links </span>
<span class="number fw-bolder">{{ total_AS_CT }}</span>
</div>
<hr />
<div class="d-flex justify-content-between align-content-center pr-5 h6">
<span class="fw-normal">Biomarker <span class="badge bg-success">characterizes</span> CT links </span>
<span class="number fw-bolder">{{ total_CT_B }}</span>
</div>
<hr />
<br />
<p class="text-muted mb-3">
<small class="fw-bold">ONTOLOGY LINKS</small>
</p>
<div *ngFor="let report of ontologyLinkGraphData">
<ngx-charts-advanced-pie-chart
[customColors]="customColors"
[view]="[600, 175]"
[results]="report.results"
class="w-100"
[label]="report.label"
>
</ngx-charts-advanced-pie-chart>
</div>
</div>
<br />
<div class="h5 mt-3 mb-3">Details</div>
<div class="details pr-4">
<div *ngIf="countsByOrgan.data.length > 1">
<div class="count-by-organ-title">
<p class="text-muted mb-3">
<small class="fw-normal">Counts by Organ</small>
</p>
<div class="display-option" #tooltip="matTooltip" matTooltip="Export as Xlsx">
<button mat-icon-button (click)="downloadReportByOrgan()" class="mr-2">
<mat-icon>get_app</mat-icon>
</button>
</div>
</div>
<table
mat-table
matSort
id="countsByOrgans"
[dataSource]="countsByOrgan"
class="mat-elevation-z8 count_by_organ_table"
>
<caption class="sr-only">
Counts by Organ
</caption>
<!-- Organ Name -->
<ng-container matColumnDef="organName">
<th mat-sort-header mat-header-cell *matHeaderCellDef id="organName">Organ</th>
<td mat-cell *matCellDef="let element">
{{ element.organName | titlecase }}
</td>
<td mat-footer-cell *matFooterCellDef>Total Counts</td>
</ng-container>
<!-- hraVersion Column -->
<ng-container matColumnDef="tableVersion">
<th mat-header-cell *matHeaderCellDef [hidden]="true" class="center_align" id="tableVersion">
table_version
</th>
<td mat-cell [hidden]="true" *matCellDef="let element" class="center_align">
{{ element.tableVersion ? element.tableVersion : 'unknown' }}
</td>
<td mat-footer-cell [hidden]="true" class="center_align" *matFooterCellDef>
{{ '' }}
</td>
</ng-container>
<!-- ASWithNoCT Column -->
<ng-container matColumnDef="ASWithNoCT">
<th mat-header-cell *matHeaderCellDef [hidden]="true" class="center_align" id="ASWithNoCT">
ASWithNoCT
</th>
<td mat-cell [hidden]="true" *matCellDef="let element" class="center_align">
{{ element.ASWithNoCT ? element.ASWithNoCT : 0 }}
</td>
<td mat-footer-cell class="center_align" [hidden]="true" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'ASWithNoCT') }}
</td>
</ng-container>
<!-- CTWithNoB Column -->
<ng-container matColumnDef="CTWithNoB">
<th mat-header-cell *matHeaderCellDef [hidden]="true" class="center_align" id="CTWithNoB">
CTWithNoB
</th>
<td mat-cell [hidden]="true" *matCellDef="let element" class="center_align">
{{ element.CTWithNoB ? element.CTWithNoB : 0 }}
</td>
<td mat-footer-cell class="center_align" [hidden]="true" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'CTWithNoB') }}
</td>
</ng-container>
<!-- ASWithNoLink Column -->
<ng-container matColumnDef="ASWithNoLink">
<th mat-header-cell *matHeaderCellDef [hidden]="true" class="center_align" id="ASWithNoLink">
ASWithNoLink
</th>
<td mat-cell [hidden]="true" *matCellDef="let element" class="center_align">
{{ element.ASWithNoLink ? element.ASWithNoLink : 0 }}
</td>
<td mat-footer-cell class="center_align" [hidden]="true" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'ASWithNoLink') }}
</td>
</ng-container>
<!-- CTWithNoLink Column -->
<ng-container matColumnDef="CTWithNoLink">
<th mat-header-cell *matHeaderCellDef [hidden]="true" class="center_align" id="CTWithNoLink">
CTWithNoLink
</th>
<td mat-cell [hidden]="true" *matCellDef="let element" class="center_align">
{{ element.CTWithNoLink ? element.CTWithNoLink : 0 }}
</td>
<td mat-footer-cell class="center_align" [hidden]="true" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'CTWithNoLink') }}
</td>
</ng-container>
<!-- BWithNoLink Column -->
<ng-container matColumnDef="BWithNoLink">
<th mat-header-cell *matHeaderCellDef [hidden]="true" class="center_align" id="BWithNoLink">
BWithNoLink
</th>
<td mat-cell [hidden]="true" *matCellDef="let element" class="center_align">
{{ element.BWithNoLink ? element.BWithNoLink : 0 }}
</td>
<td mat-footer-cell class="center_align" [hidden]="true" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'BWithNoLink') }}
</td>
</ng-container>
<!-- AS_AS Column -->
<ng-container matColumnDef="AS_AS">
<th mat-header-cell *matHeaderCellDef class="center_align" id="AS_AS">AS_AS</th>
<td mat-cell *matCellDef="let element" class="center_align">
{{ element.AS_AS ? element.AS_AS : 0 }}
</td>
<td mat-footer-cell class="center_align" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'AS_AS') }}
</td>
</ng-container>
<!-- AS_CT Column -->
<ng-container matColumnDef="AS_CT">
<th mat-header-cell *matHeaderCellDef class="center_align" id="AS_CT">AS_CT</th>
<td mat-cell *matCellDef="let element" class="center_align">
{{ element.AS_CT ? element.AS_CT : 0 }}
</td>
<td mat-footer-cell class="center_align" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'AS_CT') }}
</td>
</ng-container>
<!-- CT_BM Column -->
<ng-container matColumnDef="CT_BM">
<th mat-header-cell *matHeaderCellDef class="center_align" id="CT_BM">CT_BM</th>
<td mat-cell *matCellDef="let element" class="center_align">
{{ element.CT_BM ? element.CT_BM : 0 }}
</td>
<td mat-footer-cell class="center_align" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'CT_BM') }}
</td>
</ng-container>
<!-- Anatomical Structures Column -->
<ng-container matColumnDef="anatomicalStructures">
<th mat-header-cell *matHeaderCellDef class="center_align" id="anatomicalStructures">AS</th>
<td mat-cell *matCellDef="let element" class="center_align">
{{ element.anatomicalStructures ? element.anatomicalStructures : 0 }}
</td>
<td mat-footer-cell class="center_align" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'anatomicalStructures') }}
</td>
</ng-container>
<!-- Cell Types Column -->
<ng-container matColumnDef="cellTypes">
<th mat-header-cell *matHeaderCellDef class="center_align" id="cellTypes">CT</th>
<td mat-cell *matCellDef="let element" class="center_align">
{{ element.cellTypes ? element.cellTypes : 0 }}
</td>
<td mat-footer-cell class="center_align" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'cellTypes') }}
</td>
</ng-container>
<!-- Biomarkers Columns -->
<ng-container matColumnDef="b_total">
<th mat-header-cell *matHeaderCellDef class="center_align" id="b_total">B_Total</th>
<td mat-cell *matCellDef="let element" class="center_align">
{{ element.biomarkers ? element.biomarkers : 0 }}
</td>
<td mat-footer-cell class="center_align" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, 'biomarkers') }}
</td>
</ng-container>
<ng-container *ngFor="let biomarker of biomarkersSeperateNames" matColumnDef="{{ biomarker.name }}">
<th mat-header-cell class="center_align" id="{{ biomarker.name }}" *matHeaderCellDef>
{{ biomarker.type }}
</th>
<td mat-cell class="center_align" *matCellDef="let element">
{{ element[biomarker.name] ? element[biomarker.name] : 0 }}
</td>
<td mat-footer-cell class="center_align" *matFooterCellDef>
{{ getTotals(countsByOrgan.data, biomarker.name) }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
<tr mat-footer-row *matFooterRowDef="displayedColumns"></tr>
</table>
</div>
<p class="text-muted my-3">
<small class="fw-normal">Alphabetically sorted listings of all AS, CT, & B</small>
</p>
<mat-expansion-panel [expanded]="false">
<mat-expansion-panel-header>
<mat-panel-title>
<div class="h6 m-0">
Unique Anatomical Structures
<span class="text-muted">({{ reportData.anatomicalStructures.length }})</span>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<div class="mt-2">
<div
*ngFor="let a of reportData.anatomicalStructures | orderBy: 'structure'"
class="py-1 d-flex justify-content-between align-items-center"
>
<div class="h6 pr-2">
<span class="fw-normal"
>{{ a.structure }}
<span class="text-muted">({{ a.label ? a.label : 'label not found' }})</span></span
>
</div>
<div>
<span>{{ a.uberon }}</span>
</div>
</div>
</div>
</mat-expansion-panel>
<br />
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<div class="h6 m-0">
Unique Cell Types
<span class="text-muted">({{ reportData.cellTypes.length }})</span>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<div class="mt-2">
<div
*ngFor="let a of reportData.cellTypes | orderBy: 'structure'"
class="py-1 d-flex justify-content-between align-items-center"
>
<div class="h6">
<span class="fw-normal"
>{{ a.structure }}
<span class="text-muted">({{ a.label ? a.label : 'label not found' }})</span></span
>
</div>
<div>
<span>{{ a.link }}</span>
</div>
</div>
</div>
</mat-expansion-panel>
<br />
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<div class="h6 m-0">
Unique biomarkers
<span class="text-muted">({{ reportData.biomarkers.length }})</span>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<div class="mt-2">
<div
*ngFor="let a of reportData.biomarkers | orderBy: 'structure'"
class="py-1 d-flex justify-content-between align-items-center"
>
<div class="h6">
<span class="fw-normal">{{ a.structure }}</span>
</div>
<div>
<span>{{ a.link }}</span>
</div>
</div>
</div>
</mat-expansion-panel>
<br />
<mat-expansion-panel *ngIf="countsByOrgan.data.length === 1">
<mat-expansion-panel-header>
<mat-panel-title>
<div class="h6 m-0">Biomarker Counts By Type</div>
</mat-panel-title>
</mat-expansion-panel-header>
<div class="mt-2">
<div *ngFor="let a of biomarkersCounts" class="py-1 d-flex justify-content-between align-items-center">
<div class="h6">
<span class="fw-normal">{{ a.name + ' Biomarkers' | titlecase }}</span>
</div>
<div>
<span>{{ a.value }}</span>
</div>
</div>
</div>
</mat-expansion-panel>
<br />
<p class="text-muted my-3">
<small class="fw-normal">Missing Ontology Links</small>
</p>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<div class="h6 m-0">
Anatomical Structures with no Uberon link
<span class="text-muted">({{ reportData.ASWithNoLink.length }})</span>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<div class="mt-2">
<div
*ngFor="let a of reportData.ASWithNoLink | orderBy: 'structure'"
class="py-1 d-flex justify-content-between align-items-center"
>
<div class="h6">
<span class="fw-normal"
>{{ a.structure }}
<span class="text-muted">({{ a.label ? a.label : 'label not found' }})</span></span
>
</div>
</div>
</div>
</mat-expansion-panel>
<br />
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<div class="h6 m-0">
Cell Types with no CL link
<span class="text-muted">({{ reportData.CTWithNoLink.length }})</span>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<div class="mt-2">
<div
*ngFor="let a of reportData.CTWithNoLink | orderBy: 'structure'"
class="py-1 d-flex justify-content-between align-items-center"
>
<div class="h6">
<span class="fw-normal"
>{{ a.structure }}
<span class="text-muted">({{ a.label ? a.label : 'label not found' }})</span></span
>
</div>
</div>
</div>
</mat-expansion-panel>
<br />
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<div class="h6 m-0">
Biomarkers with no HGNC links
<span class="text-muted">({{ reportData.BWithNoLink.length }})</span>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<div class="mt-2">
<div
*ngFor="let a of reportData.BWithNoLink | orderBy: 'structure'"
class="py-1 d-flex justify-content-between align-items-center"
>
<div class="h6">
<span class="fw-normal">{{ a.structure }}</span>
</div>
</div>
</div>
</mat-expansion-panel>
<br />
</div>
<br />
</div>
</mat-tab>
<mat-tab *ngIf="!hideReportCompareTab" label="Compare Sheets">
<div *ngIf="(compareData | async)?.data?.length === 0">
<p class="i2">You have not linked any sheets to compare yet.</p>
</div>
<div *ngIf="((compareData | async)?.data?.length ?? 0) > 0">
<br />
<div *ngFor="let sheet of compareReport; let i = index">
<mat-expansion-panel hideToggle="true" [disabled]="clickButton" (click)="clickButton = false">
<mat-expansion-panel-header [style.backgroundColor]="sheet.color" class="compare">
<mat-panel-title>
<div class="text-white h6 m-0">
{{ sheet.title }}
</div>
</mat-panel-title>
<mat-panel-description>
<button mat-icon-button (click)="downloadReport(i)" class="download-report">
<mat-icon>get_app</mat-icon>
</button>
<button mat-icon-button (click)="deleteCompareSheetReport(i)" class="delete-report">
<mat-icon>delete</mat-icon>
</button>
</mat-panel-description>
</mat-expansion-panel-header>
<div class="pt-2">
<p class="sheet-description pt-2">
{{ sheet.description }}
</p>
<div class="title2">Overview</div>
<div class="overview">
<div class="i1">
<span>Identical anatomical structures: </span>
<span class="number">{{ sheet.identicalAS.length }}</span>
</div>
<div class="i1">
<span>Identical cell types: </span>
<span class="number">{{ sheet.identicalCT.length }}</span>
</div>
<div class="i1">
<span>Identical biomarkers: </span>
<span class="number">{{ sheet.identicalB.length }}</span>
</div>
<br />
<div class="i1">
<span>New anatomical structures: </span>
<span class="number">{{ sheet.newAS.length }}</span>
</div>
<div class="i1">
<span>New cell types: </span>
<span class="number">{{ sheet.newCT.length }}</span>
</div>
<div class="i1">
<span>New biomarkers: </span>
<span class="number">{{ sheet.newB.length }}</span>
</div>
<br />
</div>
<div class="title2">Details</div>
<mat-expansion-panel>
<mat-expansion-panel-header class="mb-2">
<div class="report-expansion-header">Identical Anatomical Structures</div>
</mat-expansion-panel-header>
<div class="i2" *ngFor="let ia of sheet.identicalAS">
{{ ia }}
</div>
</mat-expansion-panel>
<mat-expansion-panel class="mt-2">
<mat-expansion-panel-header class="mb-2">
<div class="report-expansion-header">Identical Cell Types</div>
</mat-expansion-panel-header>
<div class="i2" *ngFor="let ia of sheet.identicalCT">
{{ ia }}
</div>
</mat-expansion-panel>
<mat-expansion-panel class="mt-2">
<mat-expansion-panel-header class="mb-2">
<div class="report-expansion-header">Identical Biomarkers</div>
</mat-expansion-panel-header>
<div class="i2" *ngFor="let ia of sheet.identicalB">
{{ ia }}
</div>
</mat-expansion-panel>
<mat-expansion-panel class="mt-2">
<mat-expansion-panel-header class="mb-2">
<div class="report-expansion-header">New Anatomical Structures</div>
</mat-expansion-panel-header>
<div class="i2" *ngFor="let ia of sheet.newAS">
{{ ia }}
</div>
</mat-expansion-panel>
<mat-expansion-panel class="mt-2">
<mat-expansion-panel-header class="mb-2">
<div class="report-expansion-header">New Cell Types</div>
</mat-expansion-panel-header>
<div class="i2" *ngFor="let ia of sheet.newCT">
{{ ia }}
</div>
</mat-expansion-panel>
<mat-expansion-panel class="mt-2">
<mat-expansion-panel-header class="mb-2">
<div class="report-expansion-header">New Biomarkers</div>
</mat-expansion-panel-header>
<div class="i2" *ngFor="let ia of sheet.newB">
{{ ia }}
</div>
</mat-expansion-panel>
</div>
</mat-expansion-panel>
<br />
</div>
</div>
</mat-tab>
</mat-tab-group>
</div>
</app-sidenav>
./report.component.scss
.report {
width: 43.75rem;
}
.download-report {
color: white;
}
.count_by_organ_table {
width: 100%;
box-shadow: none;
border: solid 1px #f5f5f5;
border-collapse: collapse;
}
tr.mat-footer-row {
font-weight: bold;
}
.count-by-organ-title {
display: flex;
justify-content: space-between;
}
th.mat-header-cell:last-of-type,
td.mat-cell:last-of-type,
td.mat-footer-cell:last-of-type {
padding-right: unset !important;
}
.center_align {
text-align: center;
}
.delete-report {
margin-left: 0.25rem;
margin-top: -0.1875rem;
color: white;
}
.sheet-description {
color: grey;
font-style: italic;
}
.report-expansion-header {
font-weight: 400;
font-size: 12pt;
}
.date {
color: grey;
font-size: 9pt;
}
hr {
margin-top: 0.625rem;
margin-bottom: 0.625rem;
}
::ng-deep .mat-list-item-content {
padding: 0 !important;
margin-top: -0.625rem;
}
::ng-deep .mat-subheader {
padding: 0 !important;
}
::ng-deep .mat-tab-body {
text-align: left;
}
.content {
margin-top: 0.9375rem;
z-index: 1;
}
.title {
font-size: 20pt;
line-height: 1.6;
}
.title1 {
font-size: 18pt;
font-weight: 400;
margin-bottom: 1.25rem;
}
.title2 {
font-size: 14pt;
font-weight: 400;
margin-bottom: 0.9375rem;
}
.i1 {
font-size: 14pt;
margin-top: 0.625rem;
margin-bottom: 0.625rem;
font-weight: 300;
}
.i1:hover {
color: #1976d2;
}
.i2 {
font-size: 12pt;
margin-top: 0.625rem;
margin-bottom: 0.625rem;
font-weight: 300;
}
.footer {
position: absolute;
bottom: 0rem;
height: 3.75rem;
left: 0rem;
right: 0rem;
padding-left: 0.3125rem;
padding-right: 0.3125rem;
padding-top: 0.9375rem;
display: flex;
z-index: 2;
align-items: center;
justify-content: space-between;
border-top: 0.0625rem solid rgb(203, 203, 203);
}
// .overview {
// font-size: 14pt;
// }
.number {
font-weight: 400;
padding-right: 3.1rem;
}
.details {
font-size: 14pt;
}
.mat-expansion-panel:not([class*='mat-elevation-z']) {
box-shadow: none;
}
.mat-expansion-panel-header:not(.compare) {
background: #f5f5f5 !important;
}
.mat-expansion-panel-header:hover {
opacity: 0.85 !important;
}
.compare .mat-expansion-panel-header-description {
justify-content: flex-end !important;
align-items: center;
margin-right: 0 !important;
}
.compare .mat-expansion-panel-header-description {
flex-basis: 0;
}
.compare .mat-expansion-panel-header-title {
display: flex;
align-items: center;
justify-content: space-between;
}
::ng-deep.mat-expansion-indicator {
margin-top: -0.3125rem !important;
}
.example-spacer {
flex: 1 1 auto;
}
.tabs {
position: absolute;
top: 4.0625rem;
bottom: 3.125rem;
overflow: auto;
right: 0rem;
left: 0rem;
padding-left: 0.3125rem;
padding-right: 0.3125rem;
/* height: 100%; */
/* flex: 1 1 auto; */
}
.loader {
height: 100%;
justify-content: center;
align-items: center;
display: flex;
flex-direction: column;
}
.loader > embed {
display: block;
width: 9.375rem;
height: 4.6875rem;
text-align: center;
}
.dbtn {
border: 0.0625rem solid #444a65;
border-radius: 0.5rem;
width: 100%;
transition: all 0.3s;
}
.dbtn:hover {
background-color: #444a65;
color: white;
}
// CHART
::ng-deep .advanced-pie-legend .total-value {
font-size: 1.25rem !important;
font-weight: 600;
margin-bottom: 0.3125rem !important;
}
::ng-deep .advanced-pie-legend .total-label {
font-size: 1rem !important;
margin-bottom: 0.625rem !important;
}
::ng-deep .advanced-pie-legend .legend-items-container .legend-items .legend-item .item-value {
font-size: 1rem !important;
font-weight: 600;
}
::ng-deep .advanced-pie-legend .legend-items-container .legend-items .legend-item .item-percent {
display: none;
}
// ::ng-deep .advanced-pie-legend .legend-items-container .legend-items .legend-item .item-label {
// }
::ng-deep .advanced-pie-legend .legend-items-container .legend-items .legend-item .item-value {
padding-bottom: 0.3125rem;
padding-top: 0.3125rem;
}
.badge-primary {
background-color: #377eb8 !important;
}