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
86 changes: 58 additions & 28 deletions src/Drush/Commands/RlCommands.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,13 @@ public function listExperiments(): RowsOfFields {
#[CLI\Usage(name: 'drush rl:status ab_test_button_color', description: 'Get status of button color test')]
#[CLI\Usage(name: 'drush rl:status ai_sorting-help_center_categories-block_1 --format=json', description: 'Get detailed status as JSON')]
public function status(string $experimentId, array $options = ['format' => 'yaml']): array {
return $this->analyzer->getStatus($experimentId);
try {
return $this->analyzer->getStatus($experimentId);
}
catch (\InvalidArgumentException $e) {
$this->logger()->error($e->getMessage());
throw $e;
}
}

/**
Expand Down Expand Up @@ -130,12 +136,18 @@ public function status(string $experimentId, array $options = ['format' => 'yaml
#[CLI\Usage(name: 'drush rl:perf ai_sorting-help_center_categories-block_1 --limit=10 --format=json', description: 'Get top 10 performers as JSON')]
#[CLI\Usage(name: 'drush rl:perf ab_test_headline_variants --sort=impressions', description: 'Sort by traffic volume')]
public function performance(string $experimentId, array $options = ['limit' => 20, 'sort' => 'rate']): RowsOfFields {
$data = $this->analyzer->getPerformance(
$experimentId,
(int) $options['limit'],
$options['sort']
);
return new RowsOfFields($data['arms']);
try {
$data = $this->analyzer->getPerformance(
$experimentId,
(int) $options['limit'],
$options['sort']
);
return new RowsOfFields($data['arms']);
}
catch (\InvalidArgumentException $e) {
$this->logger()->error($e->getMessage());
throw $e;
}
}

/**
Expand Down Expand Up @@ -166,12 +178,18 @@ public function performance(string $experimentId, array $options = ['limit' => 2
#[CLI\Usage(name: 'drush rl:trends ab_test_headline_variants', description: 'Get weekly trends')]
#[CLI\Usage(name: 'drush rl:trends mock_10_arm_test --period=daily --periods=14 --format=json', description: 'Get 14 days of daily data')]
public function trends(string $experimentId, array $options = ['period' => 'weekly', 'periods' => 8]): RowsOfFields {
$data = $this->analyzer->getTrends(
$experimentId,
$options['period'],
(int) $options['periods']
);
return new RowsOfFields($data['data']);
try {
$data = $this->analyzer->getTrends(
$experimentId,
$options['period'],
(int) $options['periods']
);
return new RowsOfFields($data['data']);
}
catch (\InvalidArgumentException $e) {
$this->logger()->error($e->getMessage());
throw $e;
}
}

/**
Expand All @@ -195,10 +213,16 @@ public function trends(string $experimentId, array $options = ['period' => 'week
#[CLI\Usage(name: 'drush rl:export ab_test_button_color', description: 'Export experiment data as JSON')]
#[CLI\Usage(name: 'drush rl:export mock_10_arm_test --snapshots > export.json', description: 'Export with snapshots to file')]
public function export(string $experimentId, array $options = ['snapshots' => FALSE, 'format' => 'json']): array {
return $this->analyzer->export(
$experimentId,
(bool) $options['snapshots']
);
try {
return $this->analyzer->export(
$experimentId,
(bool) $options['snapshots']
);
}
catch (\InvalidArgumentException $e) {
$this->logger()->error($e->getMessage());
throw $e;
}
}

/**
Expand All @@ -221,18 +245,24 @@ public function export(string $experimentId, array $options = ['snapshots' => FA
#[CLI\Usage(name: 'drush rl:analyze ab_test_button_color', description: 'Get full analysis with recommendations')]
#[CLI\Usage(name: 'drush rl:analyze mock_10_arm_test --format=json', description: 'Get analysis as JSON')]
public function analyze(string $experimentId, array $options = ['format' => 'yaml']): array {
$status = $this->analyzer->getStatus($experimentId);
$performance = $this->analyzer->getPerformance($experimentId, 10);
try {
$status = $this->analyzer->getStatus($experimentId);
$performance = $this->analyzer->getPerformance($experimentId, 10);

return [
'experiment' => $status['experiment'],
'status' => $status['status'],
'summary' => $status['summary'],
'value_generated' => $status['value_generated'],
'top_performers' => array_slice($performance['arms'], 0, 5),
'insights' => $performance['insights'],
'recommendation' => $this->generateRecommendation($status, $performance),
];
return [
'experiment' => $status['experiment'],
'status' => $status['status'],
'summary' => $status['summary'],
'value_generated' => $status['value_generated'],
'top_performers' => array_slice($performance['arms'], 0, 5),
'insights' => $performance['insights'],
'recommendation' => $this->generateRecommendation($status, $performance),
];
}
catch (\InvalidArgumentException $e) {
$this->logger()->error($e->getMessage());
throw $e;
}
}

/**
Expand Down
10 changes: 4 additions & 6 deletions src/Service/RlAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -299,20 +299,17 @@ public function getTrends(string $experimentId, string $period = 'weekly', int $
$trendDirection = 'declining';
}

$firstRate = $data[0]['rate'];
$lastRate = end($data)['rate'];

return [
'experiment_id' => $experimentId,
'period' => $period,
'periods_returned' => count($data),
'data' => $data,
'analysis' => [
'trend_direction' => $trendDirection,
'first_period_rate' => $firstRate,
'last_period_rate' => $lastRate,
'first_period_rate' => $data[0]['rate'] ?? 0,
'last_period_rate' => end($data)['rate'] ?? 0,
'overall_change_pct' => count($data) >= 2
? round(($lastRate - $firstRate) * 100 / max(0.01, $firstRate), 1)
? round((end($data)['rate'] - $data[0]['rate']) * 100 / max(0.01, $data[0]['rate']), 1)
: 0,
],
];
Expand Down Expand Up @@ -674,6 +671,7 @@ protected function estimateConfidence(int $conversions, int $impressions): float
$p = $conversions / $impressions;
$z = 1.96;
$denominator = 1 + $z * $z / $impressions;
$center = ($p + $z * $z / (2 * $impressions)) / $denominator;
$spread = $z * sqrt(($p * (1 - $p) + $z * $z / (4 * $impressions)) / $impressions) / $denominator;

// Narrower interval = higher confidence.
Expand Down
Loading