From 0a753e8c28673eb3eb4234bd0f07841d09992fea Mon Sep 17 00:00:00 2001 From: Benjamin Blanchard Date: Tue, 16 Dec 2025 12:12:40 -0500 Subject: [PATCH 1/3] metrics card on the data tab --- .../experiment-metrics-data-section-card.component.html | 6 +++++- .../experiment-metrics-data.component.html | 2 +- .../experiment-metrics-data.component.ts | 8 +++++--- .../experiment-query-result.component.html | 4 ---- .../experiment-query-result.component.ts | 8 ++++++-- .../src/app/features/dashboard/home/home.module.ts | 1 - 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.html b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.html index 0f9d57df43..289ac3e788 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.html +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.html @@ -16,5 +16,9 @@ - + diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.html b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.html index a97653791f..56bd05b83c 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.html +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.html @@ -1,3 +1,3 @@
-

experiment-metrics-data works!

+
diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.ts b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.ts index 3fc9dc4651..340b1bdc5d 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.ts +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.ts @@ -1,14 +1,16 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; +import { ExperimentQueryResultComponent } from '../../../../../../home/components/experiment-query-result/experiment-query-result.component'; +import { ExperimentVM } from '../../../../../../../../core/experiments/store/experiments.model'; @Component({ selector: 'app-experiment-metrics-data', - imports: [CommonModule, TranslateModule], + imports: [CommonModule, TranslateModule, ExperimentQueryResultComponent], templateUrl: './experiment-metrics-data.component.html', styleUrl: './experiment-metrics-data.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class ExperimentMetricsDataComponent { - // TODO: Implement metrics data functionality + @Input() experiment: ExperimentVM; } diff --git a/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.html b/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.html index 54e2c7e4db..baa2076a30 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.html +++ b/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.html @@ -1,8 +1,4 @@
- {{ 'home.experiment-query-result.title.text' | translate }} - {{ 'home.experiment-query-result.detail.text' | translate }} -
-
{{ 'home.experiment-query-result.main-effect.title.text' | translate }} diff --git a/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.ts b/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.ts index fa8aac2150..c1edd091ed 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.ts +++ b/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.ts @@ -15,6 +15,9 @@ import { Subscription } from 'rxjs'; import { filter } from 'rxjs/operators'; import { EXPERIMENT_STATE, EXPERIMENT_TYPE } from 'upgrade_types'; import { ExperimentFactorData } from '../../../../../core/experiment-design-stepper/store/experiment-design-stepper.model'; +import { CommonModule } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; interface FactorColumnDef { name: string; @@ -31,10 +34,11 @@ interface RowData { } @Component({ - selector: 'home-experiment-query-result', + selector: 'experiment-query-result', templateUrl: './experiment-query-result.component.html', styleUrls: ['./experiment-query-result.component.scss'], - standalone: false, + standalone: true, + imports: [CommonModule, TranslateModule, NgxChartsModule], }) export class ExperimentQueryResultComponent implements OnInit, OnDestroy { @Input() experiment: ExperimentVM; diff --git a/frontend/projects/upgrade/src/app/features/dashboard/home/home.module.ts b/frontend/projects/upgrade/src/app/features/dashboard/home/home.module.ts index 99add7cc08..1e281ad31f 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/home/home.module.ts +++ b/frontend/projects/upgrade/src/app/features/dashboard/home/home.module.ts @@ -56,7 +56,6 @@ import { MoocletPolicyEditorComponent } from './components/experiment-design/moo ImportExperimentComponent, QueriesModalComponent, CreateQueryComponent, - ExperimentQueryResultComponent, ExperimentEndCriteriaComponent, RepeatedMeasurePipe, StateTimeLogsComponent, From 2f1788f4be45e43283dc5bb742d5d95a87b25d00 Mon Sep 17 00:00:00 2001 From: doswalt Date: Wed, 17 Dec 2025 12:46:13 -0500 Subject: [PATCH 2/3] metrics data card with query result component moved into new structure --- ...t-metrics-data-section-card.component.html | 4 +- ...t-metrics-data-section-card.component.scss | 3 + ...ent-metrics-data-section-card.component.ts | 4 +- .../experiment-metrics-data.component.html | 3 - .../experiment-metrics-data.component.scss | 3 - .../experiment-metrics-data.component.ts | 14 - .../experiment-query-result.component.html | 184 ++++++++ .../experiment-query-result.component.scss | 100 ++++ .../experiment-query-result.component.ts | 432 ++++++++++++++++++ .../features/dashboard/home/home.module.ts | 2 - frontend/projects/upgrade/src/styles.scss | 2 - 11 files changed, 724 insertions(+), 27 deletions(-) delete mode 100644 frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.html delete mode 100644 frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.scss delete mode 100644 frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.ts create mode 100644 frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.html create mode 100644 frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.scss create mode 100644 frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.ts diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.html b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.html index 0f9d57df43..28f92c90d2 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.html +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.html @@ -16,5 +16,7 @@ - +
+ +
diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.scss b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.scss index e69de29bb2..112cbe419e 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.scss +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.scss @@ -0,0 +1,3 @@ +.data-container { + padding: 32px; +} diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.ts b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.ts index bdbb70b6f8..0a7490792d 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.ts +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data-section-card.component.ts @@ -6,7 +6,7 @@ import { } from '../../../../../../../shared-standalone-component-lib/components'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; -import { ExperimentMetricsDataComponent } from './experiment-metrics-data/experiment-metrics-data.component'; +import { ExperimentQueryResultComponent } from './experiment-query-result/experiment-query-result.component'; import { ExperimentService } from '../../../../../../../core/experiments/experiments.service'; import { AuthService } from '../../../../../../../core/auth/auth.service'; @@ -17,7 +17,7 @@ import { AuthService } from '../../../../../../../core/auth/auth.service'; CommonSectionCardComponent, CommonSectionCardTitleHeaderComponent, CommonSectionCardActionButtonsComponent, - ExperimentMetricsDataComponent, + ExperimentQueryResultComponent, TranslateModule, ], templateUrl: './experiment-metrics-data-section-card.component.html', diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.html b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.html deleted file mode 100644 index a97653791f..0000000000 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
-

experiment-metrics-data works!

-
diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.scss b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.scss deleted file mode 100644 index 112cbe419e..0000000000 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.data-container { - padding: 32px; -} diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.ts b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.ts deleted file mode 100644 index 3fc9dc4651..0000000000 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-metrics-data/experiment-metrics-data.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { TranslateModule } from '@ngx-translate/core'; - -@Component({ - selector: 'app-experiment-metrics-data', - imports: [CommonModule, TranslateModule], - templateUrl: './experiment-metrics-data.component.html', - styleUrl: './experiment-metrics-data.component.scss', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ExperimentMetricsDataComponent { - // TODO: Implement metrics data functionality -} diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.html b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.html new file mode 100644 index 0000000000..54e2c7e4db --- /dev/null +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.html @@ -0,0 +1,184 @@ +
+ {{ 'home.experiment-query-result.title.text' | translate }} + {{ 'home.experiment-query-result.detail.text' | translate }} +
+
+ {{ + 'home.experiment-query-result.main-effect.title.text' | translate + }} +
+
+ + {{ query.name }} + +
+
+ + +
+
+
+ + + + +
{{ factor | uppercase }}
+ + + {{ model.name }} +
+ {{ model.value }} +
+ ( n = {{ model.extra }} ) +
+
+
+
+
+
+
+
+
+ + +
+ + + {{ model.name }} +
+ {{ model.value }} +
+ ( n = {{ model.extra }} ) +
+
+
+
+
+
+
+
+ +
+ {{ + 'home.experiment-query-result.interaction-effect.title.text' | translate + }} +
+
+
+ + {{ query.name }} + +
+ + +
+
+
+ + + + + + + +
+ {{ factor | uppercase }} +
+ ({{ factor | uppercase }}) +
+
+
+
+
+ + + {{ model.series }} +
+ {{ model.value }} +
+ ( n = {{ model.participantsLogged }} ) +
+
+
+
+
+
+
+
+
+
+
+
+ + + + + {{ column.label | uppercase }} + {{ row[column.name] }} + + + + + {{ column.label | uppercase }} + {{ row[column.name] }} + + + + + + + + +
+
+
+ + + + +
+ +
+
+
diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.scss b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.scss new file mode 100644 index 0000000000..e4c453e21e --- /dev/null +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.scss @@ -0,0 +1,100 @@ +.experiment-queries-result { + padding: 0 34px 34px; + + .metric-data-text { + display: block; + } + + .main-effect-data-text { + display: block; + } + + .interaction-effect-data-text { + display: block; + } + + .metric-data-detail-text { + color: var(--grey-3); + margin-bottom: 10px; + } + + .factor-name-main { + color: var(--grey-3); + margin-top: 10px; + padding-left: 150px; + } + + .factor-name1 { + color: var(--grey-3); + margin-top: 10px; + padding-left: 150px; + padding-right: 330px; + } + + .factor-name2 { + color: var(--grey-3); + margin-bottom: -50px; + padding-left: 150px; + padding-right: 330px; + } + + .table-container { + padding: 24px 34px; + + .table { + mat-cell, + mat-header-cell { + justify-content: flex-start !important; + } + } + + &-query .query-table { + justify-content: space-between; + + .mat-mdc-header-cell { + justify-content: left; + } + + .mat-mdc-cell { + justify-content: left; + } + } + } + + ::ng-deep .ngx-charts { + ::ng-deep text { + font-family: 'Open Sans'; + font-weight: 600; + display: flex; + flex-grow: 1; + } + } + + ::ng-deep .ngx-charts g.line-chart > g:last-of-type > g:nth-child(n) g.line-series > path { + stroke-width: 5px; + } + + ::ng-deep { + .legend-labels { + background: none !important; + margin-left: 50px; + } + } + + // ::ng-deep{ + // .legend-label-text{ + // color: white; + // } + // } + + .loading-container { + display: flex; + flex-grow: 1; + align-items: center; + justify-content: center; + } + + ::ng-deep .mat-expansion-panel-body { + padding: 0 0 16px; + } +} diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.ts b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.ts new file mode 100644 index 0000000000..2e88952c45 --- /dev/null +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.ts @@ -0,0 +1,432 @@ +import { Component, OnInit, Input, OnDestroy } from '@angular/core'; +import { + ExperimentFactor, + ExperimentVM, + InteractionEffectGraphData, + InteractionEffectLineChartSeriesData, + InteractionEffectResult, + LevelCombinationElement, + LevelsMap, + MainEffectGraphData, + QueryResult, +} from '../../../../../../../../core/experiments/store/experiments.model'; +import { AnalysisService } from '../../../../../../../../core/analysis/analysis.service'; +import { Subscription } from 'rxjs'; +import { filter } from 'rxjs/operators'; +import { EXPERIMENT_STATE, EXPERIMENT_TYPE } from 'upgrade_types'; +import { ExperimentFactorData } from '../../../../../../../../core/experiment-design-stepper/store/experiment-design-stepper.model'; +import { CommonModule } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; +import { MatTableModule } from '@angular/material/table'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; + +interface FactorColumnDef { + name: string; + label: string; +} + +interface QueryColumnDef { + name: string; + label: string; +} + +interface RowData { + [key: string]: any; +} + +@Component({ + selector: 'experiment-query-result', + templateUrl: './experiment-query-result.component.html', + styleUrls: ['./experiment-query-result.component.scss'], + imports: [CommonModule, TranslateModule, NgxChartsModule, MatTableModule, MatProgressSpinnerModule], +}) +export class ExperimentQueryResultComponent implements OnInit, OnDestroy { + @Input() experiment: ExperimentVM; + + // chart options + colorScheme = { + domain: ['#31e8dd', '#7dc7fb', '#fedb64', '#51ed8f', '#ddaaf8', '#fd9099', '#14c9be'], + }; + + queryResults = {}; + queryFactorResults = []; + interactionEffectQueryFactorResults = []; + queryResultsSub: Subscription; + analysisSub: Subscription; + isQueryExecuting$ = this.analysisService.isQueryExecuting$; + factors: string[] = []; + queries: string[] = []; + displayedColumns: string[] = []; + factorialData = {}; + experimentType: string = null; + experimentState: EXPERIMENT_STATE; + data: { name: string; series: { name: string; value: number }[]; dot: boolean }[]; + meanData2: { name: string; value: number }[]; + meanData1: { name: string; value: number }[]; + maxLevelCount = 0; + factorColumnDefs: FactorColumnDef[] = []; + queryColumnDefs: QueryColumnDef[] = []; + dataSource: RowData[] = []; + + /** + * What we want is a flat map of "id: Level": + * { + * 'abc1': Level, + * 'd6f3': Level, + * 'a2b5': Level + * } + */ + levels: LevelsMap = {}; + + constructor(private analysisService: AnalysisService) {} + + ngOnInit() { + const queryIds = []; + this.experimentType = this.experiment.type; + + if (this.experimentType === EXPERIMENT_TYPE.FACTORIAL) { + this.setMaxLevelsCount(); + // sort the factors: + this.experiment.factors = this.sortFactorsByOrderAscending(this.experiment.factors); + this.experiment.factors.map((factor) => { + this.factors.push(factor?.name); + this.displayedColumns.push(factor?.name); + }); + this.experiment.queries.forEach((query) => { + this.queries.push(query?.name); + this.displayedColumns.push(query?.name); + }); + this.levels = this.createLevelsMap(this.experiment.factors); // make a flat lookup map one time + } else { + this.setConditionCount(); + } + this.queryResults = this.experiment.queries.map((query) => { + queryIds.push(query.id); + return { + [query.id]: [], + }; + }); + + this.analysisSub = this.analysisService.experimentQueryResult$(this.experiment.id).subscribe((queryResults) => { + if (queryResults && queryResults.length) { + const queryIds = queryResults.map((queryResult) => queryResult.id); + this.analysisService.executeQuery(queryIds); + } + }); + this.queryResultsSub = this.analysisService.queryResult$.pipe(filter((result) => !!result)).subscribe((result) => { + // main effect graph data + this.populateMainEffectGraphData(result); + + // interactive effect graph data + if (this.factors.length <= 2) { + this.populateInteractionGraphData(result); + } else { + this.createMultiFactorQueryTableData(result); + } + }); + } + + createMultiFactorQueryTableData(result) { + const levelCombinationTable = this.factorDataToConditions(this.experiment.factors); + result.forEach((res) => { + // fill the result values for each query: + res.interactionEffect.forEach((data) => { + // levels of the condition: + const levels: LevelCombinationElement[] = this.getLevels(data.conditionId); + }); + }); + // Define factor columns dynamically + this.factors.forEach((factor, factorIndex) => { + const columnName = `factor${factorIndex + 1}`; + const columnLabel = factor; + this.factorColumnDefs.push({ name: columnName, label: columnLabel }); + }); + + // Define query columns dynamically + this.queries.forEach((query, queryIndex) => { + const columnName = `query${queryIndex + 1}`; + const columnLabel = query; + this.queryColumnDefs.push({ name: columnName, label: columnLabel }); + }); + + // Define data rows dynamically + levelCombinationTable.forEach((levels) => { + const rowData: RowData = {}; + this.factorColumnDefs.forEach((factorColumnDef, factorColumnDefIndex) => { + rowData[factorColumnDef.name] = levels[factorColumnDefIndex].level; + }); + this.dataSource.push(rowData); + }); + + result.forEach((res) => { + res.interactionEffect.forEach((data, dataIndex) => { + const rowData: RowData = {}; + this.queryColumnDefs.forEach((queryColumnDef) => { + rowData[queryColumnDef.name] = data.result; + }); + this.dataSource[dataIndex] = { ...this.dataSource[dataIndex], ...rowData }; + }); + }); + } + + // Get an array of all columns for the table header + get headerColumns(): string[] { + const factorColumnNames = this.factorColumnDefs.map((column) => column.name); + const queryColumnNames = this.queryColumnDefs.map((column) => column.name); + return [...factorColumnNames, ...queryColumnNames]; + } + + // Get an array of all columns for the data rows + get dataColumns(): string[] { + return this.headerColumns; + } + + sortFactorsByOrderAscending(factors: ExperimentFactor[]): ExperimentFactor[] { + return factors + .slice() + .sort((factorA, factorB) => (factorA.order > factorB.order ? 1 : factorB.order > factorA.order ? -1 : 0)); + } + + populateMainEffectGraphData(result: QueryResult[]) { + result.forEach((res) => { + let simpleExperimentResultData: MainEffectGraphData[] = []; + const factorialExperimentResultData: MainEffectGraphData[][] = []; + let factorIndex; + this.experiment.factors.forEach((factor, factorIndex) => { + factorialExperimentResultData[factorIndex] = []; + }); + if (this.experimentType === EXPERIMENT_TYPE.FACTORIAL) { + res.mainEffect.forEach((data) => { + factorIndex = this.getFactorIndex(data.levelId); + const resData = { + name: this.getLevelName(data.levelId), + value: Math.round(Number(data.result) * 100) / 100, + extra: Number(data.participantsLogged), + }; + factorialExperimentResultData[factorIndex].push(resData); + }); + + factorialExperimentResultData.forEach((factorialExperimentResData, index) => { + factorialExperimentResultData[index] = this.formatEmptyBar(factorialExperimentResData); + }); + + this.factors.forEach((factor, factorIndex) => { + this.queryFactorResults[factorIndex] = { + ...this.queryFactorResults[factorIndex], + [res.id]: factorialExperimentResultData[factorIndex], + }; + }); + } else { + simpleExperimentResultData = res.mainEffect.map((data) => ({ + name: this.getConditionCode(data.conditionId), + value: Math.round(Number(data.result) * 100) / 100, + extra: Number(data.participantsLogged), + })); + simpleExperimentResultData = this.formatEmptyBar(simpleExperimentResultData); + this.queryResults = { + ...this.queryResults, + [res.id]: simpleExperimentResultData, + }; + } + return { + [res.id]: simpleExperimentResultData, + }; + }); + } + + populateInteractionGraphData(result: QueryResult[]) { + result.forEach((res) => { + const resultData1: string[] = []; + const resultData2: string[] = []; + let emptySeries1: InteractionEffectGraphData[] = []; + let emptySeries2: InteractionEffectGraphData[] = []; + if (this.experimentType === EXPERIMENT_TYPE.FACTORIAL) { + // prepare all combination series with 0 result + // sort the factors: + this.experiment.factors = this.sortFactorsByOrderAscending(this.experiment.factors); + this.experiment.factors.map((factor, index) => { + factor.levels.map((level) => { + const levelName = level.name; + // collect level names in 2 list + index === 0 ? resultData1.push(levelName) : resultData2.push(levelName); + }); + }); + + // factor 1 with factor 2 + emptySeries1 = this.prepareEmptySeriesInteractionGraphData(resultData1, resultData2); + // factor 2 with factor 1 + emptySeries2 = this.prepareEmptySeriesInteractionGraphData(resultData2, resultData1); + + // fill the result values for each query: + const resData = []; + res.interactionEffect.forEach((data) => { + // levels of the condition: + const levels: LevelCombinationElement[] = this.getLevels(data.conditionId); + resData[0] = emptySeries1; + resData[1] = emptySeries2; + resData[0] = this.populateLineChartSeries(emptySeries1, data, levels, 1); + resData[1] = this.populateLineChartSeries(emptySeries2, data, levels, 0); + }); + this.factors.forEach((factor, factorIndex) => { + this.interactionEffectQueryFactorResults[factorIndex] = { + ...this.interactionEffectQueryFactorResults[factorIndex], + [res.id]: resData[factorIndex], + }; + }); + } + }); + } + + prepareEmptySeriesInteractionGraphData(resultData1: string[], resultData2: string[]): InteractionEffectGraphData[] { + const emptySeries: InteractionEffectGraphData[] = []; + resultData1.forEach((level1) => { + const series: InteractionEffectLineChartSeriesData[] = []; + resultData2.forEach((level2) => { + series.push({ + name: level2, + value: 0, + participantsLogged: 0, + }); + }); + emptySeries.push({ + name: level1, + series: series, + dot: true, + }); + }); + return emptySeries; + } + + populateLineChartSeries( + resData: InteractionEffectGraphData[], + data: InteractionEffectResult, + levels: LevelCombinationElement[], + factorNumber: number + ): InteractionEffectGraphData[] { + const alternateFactorNumber = factorNumber === 0 ? 1 : 0; + resData.map((result) => { + const factorIndex = result.name === levels[factorNumber].level.name ? alternateFactorNumber : factorNumber; + return result.series.map((level) => { + if (level.name === levels[factorIndex].level.name) { + level.value = Math.round(Number(data.result) * 100) / 100; + level.participantsLogged = Number(data.participantsLogged); + } + }); + }); + return resData; + } + + setMaxLevelsCount() { + this.experiment.factors.forEach((factor) => { + const levelCount = factor.levels.length; + if (levelCount > this.maxLevelCount) { + this.maxLevelCount = levelCount; + } + }); + } + + setConditionCount() { + this.maxLevelCount = this.experiment.conditions.length; + } + + getFactorIndex(levelId: string): number { + let factorIndex; + this.experiment.factors = this.sortFactorsByOrderAscending(this.experiment.factors); + this.experiment.factors.forEach((factor, index) => { + factor.levels.forEach((level) => { + if (level.id === levelId) { + factorIndex = index; + } + }); + }); + return factorIndex; + } + + isResultExist(queryId: string): boolean { + let result = this.queryResults[queryId]; + if (result) { + result = result.filter((res) => typeof res.name === 'string'); + return result.length === 0; + } + return false; + } + + getConditionCode(conditionId: string) { + return this.experiment.conditions.reduce( + (acc, condition) => (condition.id === conditionId ? (acc = condition.conditionCode as any) : acc), + null + ); + } + + getLevels(conditionId: string): LevelCombinationElement[] { + return this.experiment.conditions.reduce( + (acc, condition) => (condition.id === conditionId ? (acc = condition.levelCombinationElements as any) : acc), + null + ); + } + + createLevelsMap(factors: ExperimentFactor[]): LevelsMap { + return factors.reduce((levelsMap, factor) => { + factor.levels.forEach((level) => { + levelsMap[level.id] = level; + }); + return levelsMap; + }, {}); + } + + getLevelName(levelId: string): string { + const level = this.levels[levelId]; + return level?.name || ''; + } + + // remove empty series data labels + formateXAxisLabel(value: number) { + return !isNaN(value) ? '' : value; + } + + formateYAxisLabel(value: number) { + return value === 0.5 || value === 1.5 ? '' : value; + } + + formatEmptyBar(data: MainEffectGraphData[]) { + const emptyBars: MainEffectGraphData[] = []; + // Decide number of bars by inserting empty bars in case data not present: + for (let i = 0; i < this.maxLevelCount - data.length; i++) { + emptyBars.push({ + name: i.toString(), + value: 0, + extra: 0, + }); + } + return [...data, ...emptyBars]; + } + + factorDataToConditions(factorsData: ExperimentFactorData[], levelsCombinationData: any[] = []) { + // return if no data in factors + if (factorsData.length === 0) { + return [levelsCombinationData]; + } else { + // taking the 1st factor + const currentFactor = factorsData[0]; + const levelPermutations = []; + + for (let i = 0; i < currentFactor.levels.length; i++) { + const levelName = currentFactor.levels[i].name; + // taking level of current factor and processing on other factors + const remainingLevelsPermutations = this.factorDataToConditions(factorsData.slice(1), [ + ...levelsCombinationData, + { level: levelName }, + ]); + levelPermutations.push(...remainingLevelsPermutations); + } + return levelPermutations; + } + } + + ngOnDestroy() { + this.queryResultsSub.unsubscribe(); + this.analysisSub.unsubscribe(); + this.analysisService.setQueryResult(null); + } +} diff --git a/frontend/projects/upgrade/src/app/features/dashboard/home/home.module.ts b/frontend/projects/upgrade/src/app/features/dashboard/home/home.module.ts index 99add7cc08..377ad6301b 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/home/home.module.ts +++ b/frontend/projects/upgrade/src/app/features/dashboard/home/home.module.ts @@ -27,7 +27,6 @@ import { QueriesModalComponent } from './components/modal/queries-modal/queries- import { CreateQueryComponent } from './components/create-query/create-query.component'; import { OperationPipe } from '../../../shared/pipes/operation.pipe'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; -import { ExperimentQueryResultComponent } from './components/experiment-query-result/experiment-query-result.component'; import { ExperimentEndCriteriaComponent } from './components/modal/experiment-end-criteria/experiment-end-criteria.component'; import { RepeatedMeasurePipe } from './pipes/repeated-measure.pipe'; import { ImportExperimentComponent } from './components/modal/import-experiment/import-experiment.component'; @@ -56,7 +55,6 @@ import { MoocletPolicyEditorComponent } from './components/experiment-design/moo ImportExperimentComponent, QueriesModalComponent, CreateQueryComponent, - ExperimentQueryResultComponent, ExperimentEndCriteriaComponent, RepeatedMeasurePipe, StateTimeLogsComponent, diff --git a/frontend/projects/upgrade/src/styles.scss b/frontend/projects/upgrade/src/styles.scss index b2981f5c4c..3e77e42464 100755 --- a/frontend/projects/upgrade/src/styles.scss +++ b/frontend/projects/upgrade/src/styles.scss @@ -36,7 +36,6 @@ @import './app/features/dashboard/home/components/modal/queries-modal/queries-modal.theme.scss'; @import './app/shared/components/query-result/query-result.theme.scss'; @import './app/features/dashboard/profile/components/metrics/metrics.theme.scss'; -@import './app/features/dashboard/home/components/experiment-query-result/experiment-query-result.theme.scss'; @import './app/features/dashboard/home/components/modal/experiment-end-criteria/experiment-ending-criteria.theme.scss'; @import './app/features/dashboard/home/components/modal/import-experiment/import-experiment.theme.scss'; @import './app/features/dashboard/home/components/modal/state-time-logs/state-time-logs.theme.scss'; @@ -71,7 +70,6 @@ @include queries-modal-component-theme($theme); @include query-result-component-theme($theme); @include metrics-component-theme($theme); - @include experiment-query-result-component-theme($theme); @include experiment-ending-criteria-component-theme($theme); @include import-experiment-component-theme($theme); @include state-time-logs-modal-component-theme($theme); From f35a446a001c36434ac8282c45e6192a659cb708 Mon Sep 17 00:00:00 2001 From: doswalt Date: Tue, 6 Jan 2026 12:57:41 -0500 Subject: [PATCH 3/3] redo removal of header in component --- .../experiment-query-result.component.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.html b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.html index 54e2c7e4db..baa2076a30 100644 --- a/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.html +++ b/frontend/projects/upgrade/src/app/features/dashboard/experiments/pages/experiment-details-page/experiment-details-page-content/experiment-metrics-data-section-card/experiment-query-result/experiment-query-result.component.html @@ -1,8 +1,4 @@
- {{ 'home.experiment-query-result.title.text' | translate }} - {{ 'home.experiment-query-result.detail.text' | translate }} -
-
{{ 'home.experiment-query-result.main-effect.title.text' | translate }}