Skip to content
Snippets Groups Projects
Unverified Commit f6b24928 authored by Eileen McNaughton's avatar Eileen McNaughton Committed by GitHub
Browse files

Merge pull request #14045 from totten/master-ab-winning

(REF; #873) MailingAB - Migrate "copy winner" logic from JS to PHP
parents 824a466b 88e6ecf0
Branches
Tags
No related merge requests found
......@@ -17,20 +17,11 @@
text: ts('Submit final mailing'),
icons: {primary: 'fa-paper-plane'},
click: function() {
crmMailingMgr.mergeInto(abtest.mailings.c, abtest.mailings[mailingName], [
'name',
'recipients',
'scheduled_date'
]);
crmStatus({start: ts('Saving...'), success: ''}, abtest.save())
.then(function() {
return crmStatus({start: ts('Submitting...'), success: ts('Submitted')},
abtest.submitFinal().then(function(r) {
delete abtest.$CrmMailingABReportCnt;
return r;
}));
})
.then(function() {
crmStatus({start: ts('Submitting...'), success: ts('Submitted')},
abtest.submitFinal(abtest.mailings[mailingName].id).then(function (r) {
delete abtest.$CrmMailingABReportCnt;
}))
.then(function () {
dialogService.close('selectWinnerDialog', abtest);
});
}
......
......@@ -147,11 +147,12 @@
// Schedule the final mailing
// @return Promise CrmMailingAB
// Note: Submission may cause the server state to change. Consider abtest.submit().then(...abtest.load()...)
submitFinal: function submitFinal() {
submitFinal: function submitFinal(winner_id) {
var crmMailingAB = this;
var params = {
id: this.ab.id,
status: 'Final',
winner_id: winner_id,
approval_date: 'now',
scheduled_date: this.mailings.c.scheduled_date ? this.mailings.c.scheduled_date : 'now'
};
......
......@@ -93,6 +93,13 @@ function _civicrm_api3_mailing_a_b_submit_spec(&$spec) {
$spec['approval_date'] = $mailingFields['approval_date'];
$spec['approval_status_id'] = $mailingFields['approval_status_id'];
$spec['approval_note'] = $mailingFields['approval_note'];
$spec['winner_id'] = [
'name' => 'winner_id',
'type' => 1,
'title' => 'Winner ID',
'description' => 'The experimental mailing with the best results. If specified, values are copied to the final mailing.',
'localizable' => 0,
];
// Note: we'll pass through approval_* fields to the underlying mailing, but they may be ignored
// if the user doesn't have suitable permission. If separate approvals are required, they must be provided
// outside the A/B Test UI.
......@@ -149,6 +156,9 @@ function civicrm_api3_mailing_a_b_submit($params) {
if ($dao->status != 'Testing') {
throw new API_Exception("Cannot transition to state 'Final'");
}
if (!empty($params['winner_id'])) {
_civicrm_api3_mailing_a_b_fill_winner($params['winner_id'], $dao->mailing_id_c);
}
civicrm_api3('Mailing', 'submit', $submitParams + [
'id' => $dao->mailing_id_c,
'_skip_evil_bao_auto_recipients_' => 1,
......@@ -168,6 +178,56 @@ function civicrm_api3_mailing_a_b_submit($params) {
]);
}
/**
* @param int $winner_id
* The experimental mailing chosen as the "winner".
* @param int $final_id
* The final mailing which should imitate the "winner".
* @throws \API_Exception
*/
function _civicrm_api3_mailing_a_b_fill_winner($winner_id, $final_id) {
$copyFields = [
// 'id',
// 'name',
'campaign_id',
'from_name',
'from_email',
'replyto_email',
'subject',
'dedupe_email',
// 'recipients',
'body_html',
'body_text',
'footer_id',
'header_id',
'visibility',
'url_tracking',
'dedupe_email',
'forward_replies',
'auto_responder',
'open_tracking',
'override_verp',
'optout_id',
'reply_id',
'resubscribe_id',
'unsubscribe_id'
];
$f = CRM_Utils_SQL_Select::from('civicrm_mailing')
->where('id = #id', ['id' => $winner_id])
->select($copyFields)
->execute()
->fetchAll();
if (count($f) !== 1) {
throw new API_Exception('Invalid winner_id');
}
foreach ($f as $winner) {
civicrm_api3('Mailing', 'create', $winner + [
'id' => $final_id,
'_skip_evil_bao_auto_recipients_' => 1,
]);
}
}
/**
* Adjust Metadata for graph_stats action.
*
......
......@@ -42,9 +42,18 @@ class api_v3_MailingABTest extends CiviUnitTestCase {
parent::setUp();
$this->useTransaction(TRUE);
$this->createLoggedInUser();
$this->_mailingID_A = $this->createMailing();
$this->_mailingID_B = $this->createMailing();
$this->_mailingID_C = $this->createMailing();
$this->_mailingID_A = $this->createMailing([
'subject' => 'subject a ' . time(),
'body_text' => 'body_text a ' . time(),
]);
$this->_mailingID_B = $this->createMailing([
'subject' => 'subject b ' . time(),
'body_text' => 'body_text b ' . time(),
]);
$this->_mailingID_C = $this->createMailing([
'subject' => 'not yet ' . time(),
'body_text' => 'not yet ' . time(),
]);
$this->_groupID = $this->groupCreate();
$this->_params = array(
......@@ -154,6 +163,65 @@ class api_v3_MailingABTest extends CiviUnitTestCase {
$this->assertJobCounts(1, 1, 1);
}
/**
* Create a test. Declare the second mailing a winner. Ensure that key
* fields propagate to the final mailing.
*/
public function testSubmitWinnderId() {
$checkSyncFields = ['subject', 'body_text'];
$result = $this->groupContactCreate($this->_groupID, 20, TRUE);
$this->assertEquals(20, $result['added'], "in line " . __LINE__);
$params = $this->_params;
$params['group_percentage'] = 10;
$result = $this->callAPISuccess($this->_entity, 'create', $params);
$this->callAPISuccess('Mailing', 'create', [
'id' => $this->_mailingID_A,
'groups' => ['include' => [$this->_groupID]],
]);
$this->assertJobCounts(0, 0, 0);
$this->callAPISuccess('MailingAB', 'submit', [
'id' => $result['id'],
'status' => 'Testing',
'scheduled_date' => 'now',
'approval_date' => 'now',
]);
$this->assertJobCounts(1, 1, 0);
$b = $this->getApiValues('Mailing', ['id' => $this->_mailingID_B], $checkSyncFields);
$c = $this->getApiValues('Mailing', ['id' => $this->_mailingID_C], $checkSyncFields);
$this->assertNotEquals($b, $c);
$this->callAPISuccess('MailingAB', 'submit', [
'id' => $result['id'],
'status' => 'Final',
'winner_id' => $this->_mailingID_B,
'scheduled_date' => 'now',
'approval_date' => 'now',
]);
$this->assertJobCounts(1, 1, 1);
$b = $this->getApiValues('Mailing', ['id' => $this->_mailingID_B], $checkSyncFields);
$c = $this->getApiValues('Mailing', ['id' => $this->_mailingID_C], $checkSyncFields);
$this->assertEquals($b, $c);
}
/**
* Lookup a record via API. Return *only* the expected values.
*
* @param $entity
* @param $filter
* @param $return
* @return array
*/
protected function getApiValues($entity, $filter, $return) {
$rec = $this->callAPISuccess($entity, 'getsingle', $filter + ['return' => $return]);
return CRM_Utils_Array::subset($rec, $return);
}
/**
* @param $expectedCountA
* @param $expectedCountB
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment