Skip to content

Commit

Permalink
CRM-20958 - Fill in creation and modification times
Browse files Browse the repository at this point in the history
This implements the migration procedure from CRM-20958. To test it, I did these steps:

 * Create a new build with `4.6` (eg `civibuild create d46`)
   * Create a new case (Housing Support; case_id=1, activity_id=625-632)
     * Edit one of the activities (Long-term housing plan). Tweak the subject.
     * Edit the same activity again. Tweak the subject again.
   * Create another case (Adult Day Care Referral; case_id=2, activity_id=633-637)
     * On the CLI, delete the "Open Case" activity. (cv api activity.delete id=633)
   * Create a new standalone activity (Meeting; "Mess up my history"; act_id=638)
     * In SQL, delete its log records (`DELETE FROM civicrm_log WHERE entity_table='civicrm_activity' AND entity_id=XXX`)
   * Create a new standalone activity (Phone Call; "Do some revisions"; act_id=#639)
     * Edit the activity multiple times
   * Make a DB snapshot
     * `civibuild snapshot d46 --snapshot d46-caseactlog`
 * Create a new build with `master` (eg `civibuild create dcase` or `dmaster`)
   * On the newer build, load the snapshot and run the upgrade
     * `civibuild ut dcase /Users/myuser/buildkit/app/snapshot/d46-caseactlog/civi.sql.gz`
 * Compare results
   * On old DB, run
     * `SELECT * FROM civicrm_log WHERE entity_table='civicrm_activity' AND entity_id BETWEEN 625 AND 639 ORDER BY entity_id, id;`
   * On new DB, run
     * `SELECT * FROM civicrm_log WHERE entity_table='civicrm_activity' AND entity_id BETWEEN 625 AND 639 ORDER BY entity_id, id;` (should match above)
     * `SELECT id, created_date, modified_date FROM civicrm_activity WHERE id BETWEEN 625 AND 639;` (should match above)
     * `SELECT id, created_date, modified_date FROM civicrm_case WHERE id BETWEEN 1 AND 2;` (should match above)

Note that the data produced here is a bit dirty, but upgrade procedure commensurate output:
 * The last true modification in case `#2` was *deleting* the "Open Case" activity. However, there's no *record* of this deletion in the standard schema, so
   it doesn't influence the `modified_date` of the case. The problem is with the old data and the old tracking scheme -- not relevant to the migration.
 * Activity `#638` didn't have any log records, so its `created_date` and `modified_date` were left as `NULL`.
  • Loading branch information
totten committed Jul 26, 2017
1 parent 014093f commit 71aebd9
Showing 1 changed file with 130 additions and 0 deletions.
130 changes: 130 additions & 0 deletions CRM/Upgrade/Incremental/php/FourSeven.php
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,90 @@ public function upgrade_4_7_24($rev) {
$this->addTask('CRM-20958 - Add modified_date to civicrm_case', 'addColumn',
'civicrm_case', 'modified_date', "timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'When was the case (or closely related entity) was created or modified or deleted.'");

$this->addTask('CRM-20958 - Suspend modification tracking during upgrade', 'toggleModificationTracking', FALSE);

list($minId, $maxId) = CRM_Core_DAO::executeQuery(
"SELECT coalesce(min(id),0), coalesce(max(id),0) FROM civicrm_activity"
)->getDatabaseResult()->fetchRow();
for ($startId = $minId; $startId <= $maxId; $startId += self::BATCH_SIZE) {
$endId = $startId + self::BATCH_SIZE - 1;
$vars = array(1 => array($startId, 'Int'), 2 => array($endId, 'Int'));

$title = sprintf('CRM-20958 - Compute civicrm_activity.created_date from civicrm_log (%d => %d)', $startId, $endId);
$sql = 'UPDATE civicrm_activity
SET created_date = (SELECT MIN(l.modified_date) FROM civicrm_log l WHERE l.entity_table ="civicrm_activity" AND civicrm_activity.id = l.entity_id)
WHERE (id BETWEEN %1 AND %2)
AND created_date IS NULL
';
$this->addTask($title, 'task_executeQuery', $sql, $vars);

$title = sprintf('CRM-20958 - Compute civicrm_activity.modified_date from civicrm_log (%d => %d)', $startId, $endId);
$sql = 'UPDATE civicrm_activity
SET modified_date = (SELECT MAX(l.modified_date) FROM civicrm_log l WHERE l.entity_table ="civicrm_activity" AND civicrm_activity.id = l.entity_id)
WHERE (id BETWEEN %1 AND %2)
AND modified_date IS NULL';

$this->addTask($title, 'task_executeQuery', $sql, $vars);
}

$openCaseTypeId = CRM_Core_DAO::singleValueQuery(
'SELECT value FROM civicrm_option_value cov
INNER JOIN civicrm_option_group cog ON cov.option_group_id = cog.id
WHERE cov.name = "Open Case" and cog.name = "activity_type"'
);

list($minId, $maxId) = CRM_Core_DAO::executeQuery(
"SELECT coalesce(min(id),0), coalesce(max(id),0) FROM civicrm_case"
)->getDatabaseResult()->fetchRow();
for ($startId = $minId; $startId <= $maxId; $startId += self::BATCH_SIZE) {
$endId = $startId + self::BATCH_SIZE - 1;
$vars = array(1 => array($startId, 'Int'), 2 => array($endId, 'Int'), 3 => array($openCaseTypeId, 'Int'));

// CONSIDER: In my local system, the "Open Case" timestamps seem to be more synthetic (:00:00)
// $title = sprintf('CRM-20958 - Compute civicrm_case.created_date from "Open Case" (%d => %d)', $startId, $endId);
// $sql = 'UPDATE civicrm_case
// SET created_date = (
// SELECT MIN(a.activity_date_time)
// FROM civicrm_case_activity ca
// INNER JOIN civicrm_activity a ON (ca.activity_id = a.id)
// WHERE civicrm_case.id = ca.case_id
// AND a.activity_type_id = %3
// )
// WHERE (id BETWEEN %1 AND %2)
// AND created_date IS NULL
//';
// $this->addTask($title, 'task_executeQuery', $sql, $vars);

// In case... for some ungodly reason... the 'Open Case' activity was missing...
$title = sprintf('CRM-20958 - Compute civicrm_case.created_date from the activity log (%d => %d)', $startId, $endId);
$sql = 'UPDATE civicrm_case
SET created_date = (
SELECT MIN(l.modified_date)
FROM civicrm_case_activity ca
INNER JOIN civicrm_log l ON (l.entity_table = "civicrm_activity" AND ca.activity_id = l.entity_id)
WHERE civicrm_case.id = ca.case_id
)
WHERE (id BETWEEN %1 AND %2)
AND created_date IS NULL
';
$this->addTask($title, 'task_executeQuery', $sql, $vars);

$title = sprintf('CRM-20958 - Compute civicrm_case.modified_date from the activity log (%d => %d)', $startId, $endId);
$sql = 'UPDATE civicrm_case
SET modified_date = (
SELECT MAX(l.modified_date)
FROM civicrm_case_activity ca
INNER JOIN civicrm_log l ON (l.entity_table = "civicrm_activity" AND ca.activity_id = l.entity_id)
WHERE civicrm_case.id = ca.case_id
)
WHERE (id BETWEEN %1 AND %2)
AND modified_date IS NULL
';
$this->addTask($title, 'task_executeQuery', $sql, $vars);
}

$this->addTask('CRM-20958 - Restore modification tracking', 'toggleModificationTracking', TRUE);

return TRUE;
}

Expand Down Expand Up @@ -1217,4 +1301,50 @@ protected function checkImageUploadDir() {
return $config->imageUploadDir && $config->imageUploadURL && $check->isDirAccessible($config->imageUploadDir, $config->imageUploadURL);
}

/**
* (Queue Task Callback)
*
* Enable or disable automatic updates to the `modified_date` columns.
*
* When autopopulating the modification times, we don't want to fill in the
* current time.
*
* @param CRM_Queue_TaskContext $ctx
* @param bool $enabled
* @param
*
* @return bool
*/
public static function toggleModificationTracking($ctx, $enabled) {
if ($enabled) {
CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_case CHANGE modified_date modified_date TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_activity CHANGE modified_date modified_date TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
}
else {
CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_case CHANGE modified_date modified_date TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP');
CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_activity CHANGE modified_date modified_date TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP');
}
return TRUE;
}

/**
* (Queue Task Callback)
*
* Execute a single SQL query.
*
* @param CRM_Queue_TaskContext $ctx
* @param string $sql
* An SQL template. May include tokens `%1`, `%2`, etc.
* @param array $vars
* List of SQL parameters, as used by executeQuery().
*
* @return bool
*
* @see CRM_Core_DAO::executeQuery
*/
public static function task_executeQuery($ctx, $sql, $vars) {
CRM_Core_DAO::executeQuery($sql, $vars);
return TRUE;
}

}

0 comments on commit 71aebd9

Please sign in to comment.