diff --git a/frontend/projects/upgrade/src/app/features/dashboard/home/components/enrollment-over-time/enrollment-over-time.component.ts b/frontend/projects/upgrade/src/app/features/dashboard/home/components/enrollment-over-time/enrollment-over-time.component.ts
index e51f0b41bd..6aeee93c63 100644
--- a/frontend/projects/upgrade/src/app/features/dashboard/home/components/enrollment-over-time/enrollment-over-time.component.ts
+++ b/frontend/projects/upgrade/src/app/features/dashboard/home/components/enrollment-over-time/enrollment-over-time.component.ts
@@ -45,7 +45,22 @@ export class EnrollmentOverTimeComponent implements OnChanges, OnInit, OnDestroy
isInitialLoad = true;
showLabelOfxAxis = true;
- colors = ['#31e8dd', '#7dc7fb', '#fedb64', '#51ed8f', '#ddaaf8', '#fd9099', '#14c9be'];
+ colors = [
+ '#31e8dd',
+ '#7dc7fb',
+ '#fedb64',
+ '#51ed8f',
+ '#ddaaf8',
+ '#fd9099',
+ '#14c9be',
+ '#57ff6d',
+ '#3dffec',
+ '#fedb64',
+ '#0a6de6',
+ '#da0766',
+ '#cd8014',
+ '#fa59a1',
+ ];
colorScheme = {
domain: this.colors,
};
diff --git a/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-participants/experiment-participants.component.ts b/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-participants/experiment-participants.component.ts
index 60db5f6582..ecb5fb770c 100644
--- a/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-participants/experiment-participants.component.ts
+++ b/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-participants/experiment-participants.component.ts
@@ -175,7 +175,6 @@ export class ExperimentParticipantsComponent implements OnInit {
this.members1.removeAt(0);
this.members2.removeAt(0);
-
this.updateView1();
this.updateView2();
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 6a70b66b0e..15c851dbb9 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
@@ -13,20 +13,20 @@
-
-
- {{ factor | uppercase}} |
-
-
-
-
-
+
+
+
+
+
+ {{ factor | uppercase }} |
+
+
-
- {{ model.name }}
-
- {{ model.value }}
-
- ( n = {{ model.extra }} )
-
+
+ {{ model.name }}
+
+ {{ model.value }}
+
+ ( n = {{ model.extra }} )
+
+
-
-
+
+
+
+
+
+
+
+
+
{{ model.name }}
-
+
{{ model.value }}
-
+
( n = {{ model.extra }} )
-
-
+
-
-
-
-
-
-
- {{ model.name }}
-
- {{ model.value }}
-
- ( n = {{ model.extra }} )
-
-
-
-
-
-
@@ -120,62 +97,50 @@
-
-
- {{ factor | uppercase}} |
-
-
-
-
- ({{ factor | uppercase}}) |
-
-
-
-
-
-
- {{ model.series }}
-
- {{ model.value }}
-
- ( n = {{ model.participantsLogged }} )
-
-
+
+
+
+
+
+
+ {{ factor | uppercase }}
+ |
+
+
+
+ ({{ factor | uppercase }})
+ |
+
+
-
-
+
+
-
- {{ model.series }}
-
- {{ model.value }}
-
- ( n = {{ model.participantsLogged }} )
-
-
+
+
+ {{ model.series }}
+
+ {{ model.value }}
+
+ ( n = {{ model.participantsLogged }} )
+
+
+
@@ -184,14 +149,36 @@
-
-
-
-
-
-
-
+
= 3">
+
+
+
+
+ {{ column.label | uppercase }}
+ {{ row[column.name] }}
+
+
+
+
+ {{ column.label | uppercase }}
+ {{ row[column.name] }}
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.scss b/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.scss
index 072c0c8528..c371f73ba5 100644
--- a/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.scss
+++ b/frontend/projects/upgrade/src/app/features/dashboard/home/components/experiment-query-result/experiment-query-result.component.scss
@@ -18,18 +18,47 @@
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: 190px;
- padding-right: 280px;
+ padding-left: 150px;
+ padding-right: 330px;
}
.factor-name2 {
color: var(--grey-3);
margin-bottom: -50px;
- padding-left: 185px;
- padding-right: 275px;
+ 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-header-cell {
+ justify-content: left;
+ }
+
+ .mat-cell {
+ justify-content: left;
+ }
+ }
}
::ng-deep .ngx-charts {
@@ -41,14 +70,9 @@
}
}
- ::ng-deep .ngx-charts
- g.line-chart
- > g:last-of-type
- > g:nth-child(n)
- g.line-series
- > path {
+ ::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 {
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 2c3ea42d35..40acc41a2e 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
@@ -14,6 +14,21 @@ import { AnalysisService } from '../../../../../core/analysis/analysis.service';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { EXPERIMENT_TYPE } from 'upgrade_types';
+import { ExperimentFactorData } from '../../../../../core/experiment-design-stepper/store/experiment-design-stepper.model';
+
+interface FactorColumnDef {
+ name: string;
+ label: string;
+}
+
+interface QueryColumnDef {
+ name: string;
+ label: string;
+}
+
+interface RowData {
+ [key: string]: any;
+}
@Component({
selector: 'home-experiment-query-result',
@@ -29,13 +44,12 @@ export class ExperimentQueryResultComponent implements OnInit, OnDestroy {
};
queryResults = {};
- queryFactorResults1 = {};
- queryFactorResults2 = {};
- interactionEffectQueryFactorResults1 = {};
- interactionEffectQueryFactorResults2 = {};
+ queryFactorResults = [];
+ interactionEffectQueryFactorResults = [];
queryResultsSub: Subscription;
isQueryExecuting$ = this.analysisService.isQueryExecuting$;
- factors = [];
+ factors: string[] = [];
+ queries: string[] = [];
displayedColumns: string[] = [];
factorialData = {};
experimentType: string = null;
@@ -43,6 +57,9 @@ export class ExperimentQueryResultComponent implements OnInit, OnDestroy {
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":
@@ -66,6 +83,11 @@ export class ExperimentQueryResultComponent implements OnInit, OnDestroy {
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 {
@@ -83,10 +105,69 @@ export class ExperimentQueryResultComponent implements OnInit, OnDestroy {
this.populateMainEffectGraphData(result);
// interactive effect graph data
- this.populateInteractionGraphData(result);
+ 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, levelIndex) => {
+ 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()
@@ -95,43 +176,47 @@ export class ExperimentQueryResultComponent implements OnInit, OnDestroy {
populateMainEffectGraphData(result: QueryResult[]) {
result.forEach((res) => {
- let resultData: MainEffectGraphData[] = [];
- let resultData1: MainEffectGraphData[] = [];
- let resultData2: MainEffectGraphData[] = [];
+ 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) => {
- const factorIndex = this.getFactorIndex(data.levelId);
+ factorIndex = this.getFactorIndex(data.levelId);
const resData = {
name: this.getLevelName(data.levelId),
value: Math.round(Number(data.result) * 100) / 100,
extra: Number(data.participantsLogged),
};
- factorIndex === 0 ? resultData1.push(resData) : resultData2.push(resData);
+ 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],
+ };
});
- resultData1 = this.formatEmptyBar(resultData1);
- this.queryFactorResults1 = {
- ...this.queryFactorResults1,
- [res.id]: resultData1,
- };
- resultData2 = this.formatEmptyBar(resultData2);
- this.queryFactorResults2 = {
- ...this.queryFactorResults2,
- [res.id]: resultData2,
- };
} else {
- resultData = res.mainEffect.map((data) => ({
+ simpleExperimentResultData = res.mainEffect.map((data) => ({
name: this.getConditionCode(data.conditionId),
value: Math.round(Number(data.result) * 100) / 100,
extra: Number(data.participantsLogged),
}));
- resultData = this.formatEmptyBar(resultData);
+ simpleExperimentResultData = this.formatEmptyBar(simpleExperimentResultData);
this.queryResults = {
...this.queryResults,
- [res.id]: resultData,
+ [res.id]: simpleExperimentResultData,
};
}
return {
- [res.id]: resultData,
+ [res.id]: simpleExperimentResultData,
};
});
}
@@ -160,22 +245,21 @@ export class ExperimentQueryResultComponent implements OnInit, OnDestroy {
emptySeries2 = this.prepareEmptySeriesInteractionGraphData(resultData2, resultData1);
// fill the result values for each query:
- let resData1 = emptySeries1;
- let resData2 = emptySeries2;
+ const resData = [];
res.interactionEffect.forEach((data) => {
// levels of the condition:
const levels: LevelCombinationElement[] = this.getLevels(data.conditionId);
- resData1 = this.populateLineChartSeries(resData1, data, levels, 1);
- resData2 = this.populateLineChartSeries(resData2, data, levels, 0);
+ 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],
+ };
});
- this.interactionEffectQueryFactorResults1 = {
- ...this.interactionEffectQueryFactorResults1,
- [res.id]: resData1,
- };
- this.interactionEffectQueryFactorResults2 = {
- ...this.interactionEffectQueryFactorResults2,
- [res.id]: resData2,
- };
}
});
}
@@ -235,6 +319,7 @@ export class ExperimentQueryResultComponent implements OnInit, OnDestroy {
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) {
@@ -304,6 +389,28 @@ export class ExperimentQueryResultComponent implements OnInit, OnDestroy {
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.analysisService.setQueryResult(null);
diff --git a/frontend/projects/upgrade/src/assets/i18n/en.json b/frontend/projects/upgrade/src/assets/i18n/en.json
index 36219a603c..eb276f7fdb 100644
--- a/frontend/projects/upgrade/src/assets/i18n/en.json
+++ b/frontend/projects/upgrade/src/assets/i18n/en.json
@@ -239,7 +239,7 @@
"home.view-experiment.experiment-payloads.column-label.site": "SITE",
"home.view-experiment.experiment-payloads.column-label.target": "TARGET",
"home.view-experiment.experiment-payloads.column-label.condition": "CONDITION",
- "home.view-experiment.experiment-payloads.column-label.payload": "Payload",
+ "home.view-experiment.experiment-payloads.column-label.payload": "PAYLOAD",
"home.view-experiment.graph-type.text": "Type",
"home.view-experiment.graph-conditions.text": "Conditions",
"home.view-experiment.graph-decision-point.text": "Sites",