Skip to content

Commit

Permalink
dev/core#982 add dedupe.getstatistics api
Browse files Browse the repository at this point in the history
Per gitlab the intent is to add a bunch of api so get the logic out of the form into somewhere more accessible.

Following agreement on this I would change all calls to CRM_Dedupe_Merger::getMergeStats to call this api
and get rid of the call to CRM_Core_BAO_PrevNextCache::retrieve from getMergeStats since that retrieve
function really does differrent stuff & is being kinda pointlessly overloaded here.
  • Loading branch information
eileenmcnaughton committed May 23, 2019
1 parent ffe6b7c commit 2df7630
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 9 deletions.
18 changes: 18 additions & 0 deletions CRM/Core/BAO/PrevNextCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -496,4 +496,22 @@ public static function getPrevNextBackends() {
];
}

/**
* Generate and assign an arbitrary value to a field of a test object.
*
* This specifically supports testing the dedupe use case.
*
* @param string $fieldName
* @param array $fieldDef
* @param int $counter
* The globally-unique ID of the test object.
*/
protected function assignTestValue($fieldName, &$fieldDef, $counter) {
if ($fieldName === 'cacheKey') {
$this->cacheKey = 'merge_' . rand();
return;
}
parent::assignTestValue($fieldName, $fieldDef, $counter);
}

}
18 changes: 10 additions & 8 deletions CRM/Dedupe/Merger.php
Original file line number Diff line number Diff line change
Expand Up @@ -755,13 +755,13 @@ public static function updateMergeStats($cacheKeyString, $result = []) {
}

// get previous stats
$previousStats = CRM_Core_BAO_PrevNextCache::retrieve("{$cacheKeyString}_stats");
$previousStats = CRM_Dedupe_Merger::getMergeStats($cacheKeyString);
if (!empty($previousStats)) {
if ($previousStats[0]['merged']) {
$merged = $merged + $previousStats[0]['merged'];
if ($previousStats['merged']) {
$merged = $merged + $previousStats['merged'];
}
if ($previousStats[0]['skipped']) {
$skipped = $skipped + $previousStats[0]['skipped'];
if ($previousStats['skipped']) {
$skipped = $skipped + $previousStats['skipped'];
}
}

Expand Down Expand Up @@ -796,13 +796,15 @@ public static function resetMergeStats($cacheKeyString) {
*
* @return array
* Array of how many were merged and how many were skipped.
*
* @throws \CiviCRM_API3_Exception
*/
public static function getMergeStats($cacheKeyString) {
$stats = CRM_Core_BAO_PrevNextCache::retrieve("{$cacheKeyString}_stats");
$stats = civicrm_api3('Dedupe', 'get', ['cachekey' => "{$cacheKeyString}_stats", 'sequential' => 1])['values'];
if (!empty($stats)) {
$stats = $stats[0];
return $stats[0]['data'];
}
return $stats;
return [];
}

/**
Expand Down
151 changes: 151 additions & 0 deletions api/v3/Dedupe.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 5 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2019 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

/**
* This api exposes CiviCRM dedupe functionality.
*
* @package CiviCRM_APIv3
*/

/**
* Get rows for any cached attempted merges on the passed criteria.
*
* @param array $params
*
* @return array
*/
function civicrm_api3_dedupe_get($params) {
$sql = CRM_Utils_SQL_Select::fragment();
$sql->where(['merge_data_restriction' => "cachekey LIKE 'merge_%'"]);

if (isset($params['cachekey'])) {
// This is so bad. We actually have a camel case field name in the DB. Don't do that.
// Intercept the pain here.
$params['cacheKey'] = $params['cachekey'];
unset($params['cachekey']);
}

$options = _civicrm_api3_get_options_from_params($params, TRUE, 'PrevNextCache', 'get');
$result = _civicrm_api3_basic_get('CRM_Core_BAO_PrevNextCache', $params, FALSE, 'PrevNextCache', $sql);

if ($options['is_count']) {
return civicrm_api3_create_success($result, $params, 'PrevNextCache', 'get');
}
foreach ($result as $index => $values) {
if (isset($values['data'])) {
$result[$index]['data'] = unserialize($values['data']);
}
if (isset($values['cacheKey'])) {
$result[$index]['cachekey'] = $result[$index]['cacheKey'];
unset($result[$index]['cacheKey']);
}
}
return civicrm_api3_create_success($result, $params, 'PrevNextCache');
}

/**
* Get rows for getting dedupe cache records.
*
* @param array $params
*/
function _civicrm_api3_dedupe_get_spec(&$params) {
$params = CRM_Core_DAO_PrevNextCache::fields();
}

/**
* Delete rows for any cached attempted merges on the passed criteria.
*
* @param array $params
*
* @return array
*
* @throws \API_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*/
function civicrm_api3_dedupe_delete($params) {
return _civicrm_api3_basic_delete('CRM_Core_BAO_PrevNextCache', $params);
}

/**
* Get the statistics for any cached attempted merges on the passed criteria.
*
* @param array $params
*
* @return array
* @throws \API_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*/
function civicrm_api3_dedupe_create($params) {
$sql = CRM_Utils_SQL_Select::fragment();
$sql->where("cachekey LIKE 'merge_%'");

return _civicrm_api3_basic_create('CRM_Core_BAO_PrevNextCache', $params, 'PrevNextCache');
}

/**
* Get the statistics for any cached attempted merges on the passed criteria.
*
* @param array $params
*
* @return array
* @throws \CiviCRM_API3_Exception
*/
function civicrm_api3_dedupe_getstatistics($params) {
$stats = CRM_Dedupe_Merger::getMergeStats(CRM_Dedupe_Merger::getMergeCacheKeyString(
$params['rule_group_id'],
CRM_Utils_Array::value('group_id', $params),
CRM_Utils_Array::value('criteria', $params, []),
CRM_Utils_Array::value('check_permissions', $params, [])
));
return civicrm_api3_create_success($stats);
}

/**
* Adjust Metadata for Create action.
*
* The metadata is used for setting defaults, documentation & validation.
*
* @param array $params
* Array of parameters determined by getfields.
*/
function _civicrm_api3_dedupe_getstatistics_spec(&$params) {
$params['rule_group_id'] = [
'title' => ts('Rule Group ID'),
'api.required' => TRUE,
'type' => CRM_Utils_Type::T_INT,
];
$params['group_id'] = [
'title' => ts('Group ID'),
'api.required' => FALSE,
'type' => CRM_Utils_Type::T_INT,
];
$params['criteria'] = [
'title' => ts('Criteria'),
'description' => ts('Dedupe search criteria, as parsable by v3 Contact.get api'),
];

}
5 changes: 5 additions & 0 deletions api/v3/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,11 @@ function _civicrm_api3_get_BAO($name) {
// has enhanced access to other entities.
$name = 'Contribution';
}
if ($name === 'Dedupe') {
// Dedupe is a pseudoentity for PrevNextCache - but accessing dedupe related info
// not the other cache info like search results (which could in fact be in Redis or another cache engine)
$name = 'PrevNextCache';
}
$dao = _civicrm_api3_get_DAO($name);
if (!$dao) {
return NULL;
Expand Down
12 changes: 11 additions & 1 deletion tests/phpunit/CRM/Dedupe/MergerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ public function testBatchMergeSelectedDuplicates() {
$select = ['pn.is_selected' => 'is_selected'];
$cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($dao->id, $this->_groupId);
$pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);

$this->assertEquals(count($foundDupes), count($pnDupePairs), 'Check number of dupe pairs in prev next cache.');

// mark first two pairs as selected
Expand All @@ -191,6 +190,13 @@ public function testBatchMergeSelectedDuplicates() {
$result = CRM_Dedupe_Merger::batchMerge($dao->id, $this->_groupId, 'safe', 5, 1);
$this->assertEquals(count($result['merged']), 2, 'Check number of merged pairs.');

$stats = $this->callAPISuccess('Dedupe', 'getstatistics', [
'group_id' => $this->_groupId,
'rule_group_id' => $dao->id,
'check_permissions' => TRUE,
])['values'];
$this->assertEquals(['merged' => 2, 'skipped' => 0], $stats);

// retrieve pairs from prev next cache table
$pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);
$this->assertEquals(count($pnDupePairs), 1, 'Check number of remaining dupe pairs in prev next cache.');
Expand Down Expand Up @@ -248,6 +254,10 @@ public function testBatchMergeAllDuplicates() {
$result = CRM_Dedupe_Merger::batchMerge($dao->id, $this->_groupId, 'safe', 5, 2);
$this->assertEquals(count($result['merged']), 3, 'Check number of merged pairs.');

$stats = $this->callAPISuccess('Dedupe', 'getstatistics', [
'rule_group_id' => $dao->id,
'group_id' => $this->_groupId,
]);
// retrieve pairs from prev next cache table
$pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);
$this->assertEquals(count($pnDupePairs), 0, 'Check number of remaining dupe pairs in prev next cache.');
Expand Down
1 change: 1 addition & 0 deletions tests/phpunit/api/v3/ContactTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public function tearDown() {
'civicrm_group_contact',
'civicrm_saved_search',
'civicrm_group_contact_cache',
'civicrm_prevnext_cache',
);

$this->quickCleanup($tablesToTruncate, TRUE);
Expand Down
1 change: 1 addition & 0 deletions tests/phpunit/api/v3/SyntaxConformanceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,7 @@ public function testCustomDataGet($entityName) {
'SmsProvider' => 'Provider',
'AclRole' => 'EntityRole',
'MailingEventQueue' => 'Queue',
'Dedupe' => 'PrevNextCache',
];

$usableName = !empty($entitiesWithNamingIssues[$entityName]) ? $entitiesWithNamingIssues[$entityName] : $entityName;
Expand Down

0 comments on commit 2df7630

Please sign in to comment.