Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,16 @@
[formGroupName]="queryIndex"
style="justify-content: flex-start"
>
<mat-icon class="remove-icon" (click)="removeMetric(queryIndex)"> delete_outline </mat-icon>
<mat-icon
class="remove-icon"
[class.remove-icon--disabled]="isRewardMetric(queryIndex)"
[attr.aria-disabled]="isRewardMetric(queryIndex)"
[attr.tabindex]="isRewardMetric(queryIndex) ? -1 : 0"
(click)="!isRewardMetric(queryIndex) && removeMetric(queryIndex)"
(keydown)="$event.key === 'Enter' || $event.key === ' ' ? (!isRewardMetric(queryIndex) && removeMetric(queryIndex)) : null"
>
delete_outline
</mat-icon>
</mat-cell>
</ng-container>

Expand Down Expand Up @@ -272,47 +281,25 @@
<!-- Reward Metric Section -->
<div class="reward-metric-container" *ngIf="isMoocletExperimentDesign$ | async">
<span class="title">{{ 'home.new-experiment.metrics.reward-metric.text' | translate }}</span>
<mat-table class="reward-metric-table" [dataSource]="rewardMetricDataSource">
<!-- Metric Column -->
<ng-container matColumnDef="metricsKey">
<mat-header-cell class="ft-14-700" *matHeaderCellDef>
{{ 'query.metric.text' | translate }}
</mat-header-cell>
<mat-cell *matCellDef="let row">
{{ row.metric_Key }}
</mat-cell>
</ng-container>

<!-- Statistic Column -->
<ng-container matColumnDef="metricsOperation">
<mat-header-cell class="ft-14-700" *matHeaderCellDef>
{{ 'monitor.statistic.text' | translate }}
</mat-header-cell>
<mat-cell *matCellDef="let row">
{{ row.metric_Operation }}
</mat-cell>
</ng-container>

<!-- Description Column -->
<ng-container matColumnDef="metricsName">
<mat-header-cell class="ft-14-700" *matHeaderCellDef>
{{ 'home.new-experiment.metrics.description-header.placeholder.text' | translate }}
</mat-header-cell>
<mat-cell *matCellDef="let row">
{{ row.metric_Name }}
</mat-cell>
</ng-container>

<mat-header-row *matHeaderRowDef="rewardMetricDisplayedColumns; sticky: true"></mat-header-row>
<mat-row *matRowDef="let row; columns: rewardMetricDisplayedColumns"></mat-row>
</mat-table>

<p class="form-hint ft-12-400">
A reward metric is required for adaptive experiments.
<a class="learn-more-link" href="https://www.upgradeplatform.org/" target="_blank" rel="noopener noreferrer"
>Learn more</a
>
</p>
<div class="reward-metric-description" *ngIf="rewardMetricDataSource | async as rewardMetricData">
<div class="reward-metric-item" *ngFor="let row of rewardMetricData">
<span>
<span class="ft-14-600">Key:</span>
<span class="ft-14-400"> "{{ row.metric_Key }}"</span>
</span>
<span>
<span class="ft-14-600">Allowed Values:</span>
<span class="ft-14-400"> ["SUCCESS", "FAILURE"]</span>
</span>
<span class="ft-12-400">
Adaptive experiments require "reward" feedback to be logged from enrolled users, which will tilt the probability of experimental assignments towards conditions that receive more successful feedback. Use this key when logging metrics as a simple metric value.
</span>
<span class="ft-12-600" *ngIf="!experimentInfo">
UpGrade automatically creates this metric for your experiment when created, and a metric query for success rate will also be automatically applied.
<a class="learn-more-link" href="https://www.upgradeplatform.org/" target="_blank" rel="noopener noreferrer">Learn more</a>
</span>
</div>
</div>
</div>
</form>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
height: 515px;
overflow-y: auto;

.metric-table,
.reward-metric-table {
.metric-table {
margin-top: 5px;
max-height: 207px;
overflow: auto;
Expand Down Expand Up @@ -106,6 +105,15 @@
&:hover {
cursor: pointer;
}

&.disabled,
&.remove-icon--disabled {
opacity: 0.5;

&:hover {
cursor: not-allowed;
}
}
}

.auto-complete {
Expand Down Expand Up @@ -138,24 +146,17 @@
}

.reward-metric-container {
padding-top: 32px;
padding-top: 16px;

.reward-metric-table {
mat-cell {
align-items: center;
}
.reward-metric-description {
padding: 16px 8px 0;
color: var(--black-2);

.cdk-column-queryName {
&.mat-mdc-cell {
padding-top: 0;
}
.reward-metric-item {
display: flex;
flex-direction: column;
gap: 8px;
}
}

.form-hint {
text-indent: 24px;
margin-top: 8px;
margin-bottom: 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,17 @@ export class MonitoredMetricsComponent implements OnInit, OnChanges, OnDestroy {
});
}

isRewardMetric(queryIndex: number): boolean {
const keysFormArray = this.queries.at(queryIndex)?.get('keys') as UntypedFormArray;
const firstKey = keysFormArray?.at(0);
const metricKey = firstKey?.get('metricKey')?.value;
if (!metricKey) return false;

// Extract the key string, handling both string and object formats
const keyString = metricKey.key ? metricKey.key : metricKey;
return keyString && keyString.endsWith('_REWARD');
}

displayFn(node?: any): string | undefined {
if (node && node.key) {
return node ? node.key : undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -905,8 +905,8 @@
{{ 'monitor.statistic.text' | translate }}
</mat-header-cell>
<mat-cell class="ft-12-600" *matCellDef="let data">
<div *ngFor="let statisticOperation of data.metric_Operation">
{{ statisticOperation | titlecase }}
<div style="white-space: pre-line;">
{{ formatStatisticOperation(data.metric_Operation) }}
</div>
</mat-cell>
</ng-container>
Expand All @@ -933,43 +933,24 @@
</mat-table>
</div>

<!-- Experiment reward metric table -->
<div class="table-container table-container-metrics" *ngIf="experiment?.rewardMetricKey">
<!-- Experiment reward metric section -->
<div class="table-container" *ngIf="experiment?.rewardMetricKey">
<span class="ft-24-700">{{ 'home.new-experiment.metrics.reward-metric.text' | translate }}</span>
<mat-table [dataSource]="displayRewardMetrics" class="table">
<!-- Metric Column -->
<ng-container matColumnDef="metricsKey">
<mat-header-cell class="ft-12-700" *matHeaderCellDef>
{{ 'query.metric.text' | translate }}
</mat-header-cell>
<mat-cell class="ft-12-600" *matCellDef="let row">
{{ row.metric_Key }}
</mat-cell>
</ng-container>

<!-- Statistic Column -->
<ng-container matColumnDef="metricsOperation">
<mat-header-cell class="ft-12-700" *matHeaderCellDef>
{{ 'monitor.statistic.text' | translate }}
</mat-header-cell>
<mat-cell class="ft-12-600" *matCellDef="let row">
{{ row.metric_Operation }}
</mat-cell>
</ng-container>

<!-- Description Column -->
<ng-container matColumnDef="metricsName">
<mat-header-cell class="ft-12-700" *matHeaderCellDef>
{{ 'home.new-experiment.metrics.description-header.placeholder.text' | translate }}
</mat-header-cell>
<mat-cell class="ft-12-600" *matCellDef="let row">
{{ row.metric_Name }}
</mat-cell>
</ng-container>

<mat-header-row *matHeaderRowDef="displayedMetricsColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedMetricsColumns"></mat-row>
</mat-table>
<div class="reward-metric-description" *ngIf="displayRewardMetrics as rewardMetricData">
<div class="reward-metric-item" *ngFor="let row of rewardMetricData">
<span>
<span class="ft-14-600">Key:</span>
<span class="ft-14-400"> "{{ row.metric_Key }}"</span>
</span>
<span>
<span class="ft-14-600">Allowed Values:</span>
<span class="ft-14-400"> ["SUCCESS", "FAILURE"]</span>
</span>
<span class="ft-12-400">
Adaptive experiments require "reward" feedback to be logged from enrolled users, which will tilt the probability of experimental assignments towards conditions that receive more successful feedback. Use this key when logging metrics as a simple metric value.
</span>
</div>
</div>
</div>
</div>
</mat-tab>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,12 @@ $font-size-small: 15px;
mat-header-cell {
color: var(--grey-2);
}

.cdk-column-metricsKey,
.cdk-column-metricsOperation {
flex-direction: column;
align-items: flex-start;
}
}

pre {
Expand All @@ -403,6 +409,17 @@ $font-size-small: 15px;
margin-right: 20px;
}
}

.reward-metric-description {
padding: 16px 8px 0;
color: var(--black-2);

.reward-metric-item {
display: flex;
flex-direction: column;
gap: 8px;
}
}
}

::ng-deep .owl-dt-calendar-table .owl-dt-calendar-cell-selected {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,29 @@ export class ViewExperimentComponent implements OnInit, OnDestroy {
: '';
}

formatStatisticOperation(statisticOperations: string[]): string {
if (!statisticOperations || statisticOperations.length === 0) {
return '';
}

// Check if this is a categorical metric with comparison (length 3 or 4)
const isCategoricalWithComparison =
statisticOperations.length >= 3 && statisticOperations.some((op) => op === 'equal' || op === 'not equal');

if (isCategoricalWithComparison) {
// For categorical metrics: [..., compareFn, compareValue, operationType]
const operationType = statisticOperations[statisticOperations.length - 1];
const compareValue = statisticOperations[statisticOperations.length - 2];
const compareFn = statisticOperations[statisticOperations.length - 3];

const operator = compareFn === 'equal' ? 'Equal' : 'Not Equal';
return `${operationType}\n${operator}\n${compareValue}`;
}

// For other cases (continuous metrics, etc.), show as separate lines
return statisticOperations.join('\n');
}

ngOnDestroy() {
this.experimentSub.unsubscribe();
this.permissionsSub.unsubscribe();
Expand Down
Loading