Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
90.91% |
90 / 99 |
|
66.67% |
8 / 12 |
CRAP | |
0.00% |
0 / 1 |
| TsumegoMerger | |
90.91% |
90 / 99 |
|
66.67% |
8 / 12 |
30.68 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| checkInput | |
66.67% |
6 / 9 |
|
0.00% |
0 / 1 |
4.59 | |||
| mergeSlaveSetConnections | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| mergeStatus | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
4.01 | |||
| mergeStatuses | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| mergeTsumegoAttempts | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| mergeComments | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| mergeFavorites | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
4.02 | |||
| mergeTagConnections | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
4 | |||
| mergeTimeModeAttempts | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| mergeIssues | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| execute | |
89.19% |
33 / 37 |
|
0.00% |
0 / 1 |
3.01 | |||
| 1 | <?php |
| 2 | |
| 3 | class TsumegoMerger |
| 4 | { |
| 5 | public function __construct($masterTsumegoID, $slaveTsumegoID) |
| 6 | { |
| 7 | $this->masterTsumegoID = $masterTsumegoID; |
| 8 | $this->slaveTsumegoID = $slaveTsumegoID; |
| 9 | } |
| 10 | |
| 11 | private function checkInput(): ?array |
| 12 | { |
| 13 | $masterTsumego = ClassRegistry::init('Tsumego')->findById($this->masterTsumegoID); |
| 14 | if (!$masterTsumego) |
| 15 | return ['message' => 'Merge masterTsumego not found', 'type' => 'error']; |
| 16 | |
| 17 | $slaveTsumego = ClassRegistry::init('Tsumego')->findById($this->slaveTsumegoID); |
| 18 | if (!$slaveTsumego) |
| 19 | return ['message' => 'Slave tsumego does not exist.', 'type' => 'error']; |
| 20 | |
| 21 | if ($this->masterTsumegoID == $this->slaveTsumegoID) |
| 22 | return ['message' => 'Tsumegos already merged.', 'type' => 'error']; |
| 23 | return null; |
| 24 | } |
| 25 | |
| 26 | private function mergeSlaveSetConnections() |
| 27 | { |
| 28 | $slaveSetConnectionBrothers = ClassRegistry::init('SetConnection')->find('all', ['conditions' => ['tsumego_id' => $this->slaveTsumegoID]]); |
| 29 | foreach ($slaveSetConnectionBrothers as $slaveTsumegoBrother) |
| 30 | { |
| 31 | $slaveTsumegoBrother['SetConnection']['tsumego_id'] = $this->masterTsumegoID; |
| 32 | ClassRegistry::init('SetConnection')->save($slaveTsumegoBrother); |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | private function mergeStatus(?int $statusID1, ?int $statusID2): void |
| 37 | { |
| 38 | if (!$statusID1) |
| 39 | { |
| 40 | $tsumegoStatus = ClassRegistry::init('TsumegoStatus')->findById($statusID2)['TsumegoStatus']; |
| 41 | $tsumegoStatus['tsumego_id'] = $this->masterTsumegoID; |
| 42 | ClassRegistry::init('TsumegoStatus')->save($tsumegoStatus); |
| 43 | return; |
| 44 | } |
| 45 | |
| 46 | // second status doesn't exist, so we just keep the master one |
| 47 | if (!$statusID2) |
| 48 | return; |
| 49 | |
| 50 | $masterStatus = ClassRegistry::init('TsumegoStatus')->findById($statusID1)['TsumegoStatus']; |
| 51 | $slaveStatus = ClassRegistry::init('TsumegoStatus')->findById($statusID2)['TsumegoStatus']; |
| 52 | $masterStatus['status'] = TsumegoStatus::less($masterStatus['status'], $slaveStatus['status']) ? $slaveStatus['status'] : $masterStatus['status']; |
| 53 | ClassRegistry::init('TsumegoStatus')->save($masterStatus); |
| 54 | } |
| 55 | |
| 56 | private function mergeStatuses() |
| 57 | { |
| 58 | $statusMergeSources = Util::query(" |
| 59 | SELECT |
| 60 | user_id, |
| 61 | MAX(CASE WHEN tsumego_id = :id1 THEN id END) AS tsumego_status_id_1, |
| 62 | MAX(CASE WHEN tsumego_id = :id1 THEN status END) AS tsumego_status_1, |
| 63 | MAX(CASE WHEN tsumego_id = :id2 THEN id END) AS tsumego_status_id_2, |
| 64 | MAX(CASE WHEN tsumego_id = :id2 THEN status END) AS tsumego_status_2 |
| 65 | FROM tsumego_status |
| 66 | WHERE tsumego_id IN (:id1, :id2) |
| 67 | GROUP BY user_id |
| 68 | HAVING |
| 69 | COUNT(*) BETWEEN 1 AND 2", [':id1' => $this->masterTsumegoID, ':id2' => $this->slaveTsumegoID]); |
| 70 | foreach ($statusMergeSources as $statusMergeSource) |
| 71 | $this->mergeStatus($statusMergeSource['tsumego_status_id_1'], $statusMergeSource['tsumego_status_id_2']); |
| 72 | } |
| 73 | |
| 74 | private function mergeTsumegoAttempts() |
| 75 | { |
| 76 | $slaveAttempts = ClassRegistry::init('TsumegoAttempt')->find('all', ['conditions' => ['tsumego_id' => $this->slaveTsumegoID]]); |
| 77 | foreach ($slaveAttempts as $slaveAttempt) |
| 78 | { |
| 79 | $slaveAttempt['TsumegoAttempt']['tsumego_id'] = $this->masterTsumegoID; |
| 80 | ClassRegistry::init('TsumegoAttempt')->save($slaveAttempt); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | private function mergeComments() |
| 85 | { |
| 86 | $slaveComments = ClassRegistry::init('TsumegoComment')->find('all', ['conditions' => ['tsumego_id' => $this->slaveTsumegoID]]); |
| 87 | foreach ($slaveComments as $slaveComment) |
| 88 | { |
| 89 | $slaveComment['TsumegoComment']['tsumego_id'] = $this->masterTsumegoID; |
| 90 | ClassRegistry::init('TsumegoComment')->save($slaveComment); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | private function mergeFavorites() |
| 95 | { |
| 96 | $favoritesMergeSource = Util::query(" |
| 97 | SELECT |
| 98 | user_id, |
| 99 | MAX(CASE WHEN tsumego_id = :id1 THEN id END) AS favorite_id_1, |
| 100 | MAX(CASE WHEN tsumego_id = :id2 THEN id END) AS favorite_id_2 |
| 101 | FROM favorite |
| 102 | WHERE tsumego_id IN (:id1, :id2) |
| 103 | GROUP BY user_id |
| 104 | HAVING |
| 105 | COUNT(*) BETWEEN 1 AND 2", [':id1' => $this->masterTsumegoID, ':id2' => $this->slaveTsumegoID]); |
| 106 | foreach ($favoritesMergeSource as $favoriteMergeSource) |
| 107 | { |
| 108 | // slave is empty, nothing to do |
| 109 | if (!$favoriteMergeSource['favorite_id_2']) |
| 110 | continue; |
| 111 | |
| 112 | // master is empty, we change the slave to master |
| 113 | if (!$favoriteMergeSource['favorite_id_1']) |
| 114 | { |
| 115 | $favorite = ClassRegistry::init('Favorite')->findById($favoriteMergeSource['favorite_id_2'])['Favorite']; |
| 116 | $favorite['tsumego_id'] = $this->masterTsumegoID; |
| 117 | ClassRegistry::init('Favorite')->save($favorite); |
| 118 | continue; |
| 119 | } |
| 120 | // when both master and slave is present, we don't have to do anything, the slave one will be removed by |
| 121 | // foreign key cascade |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | private function mergeTagConnections() |
| 126 | { |
| 127 | $tagMergeSources = Util::query(" |
| 128 | SELECT |
| 129 | tag_id, |
| 130 | MAX(CASE WHEN tsumego_id = :id1 THEN id END) AS tag_connection_id_1, |
| 131 | MAX(CASE WHEN tsumego_id = :id2 THEN id END) AS tag_connection_id_2 |
| 132 | FROM tag_connection |
| 133 | WHERE tsumego_id IN (:id1, :id2) |
| 134 | GROUP BY tag_id |
| 135 | HAVING |
| 136 | COUNT(*) BETWEEN 1 AND 2", [':id1' => $this->masterTsumegoID, ':id2' => $this->slaveTsumegoID]); |
| 137 | foreach ($tagMergeSources as $tagMergeSource) |
| 138 | { |
| 139 | // slave is empty, nothing to do |
| 140 | if (!$tagMergeSource['tag_connection_id_2']) |
| 141 | continue; |
| 142 | |
| 143 | // master is empty, we change the slave to master |
| 144 | if (!$tagMergeSource['tag_connection_id_1']) |
| 145 | { |
| 146 | $tagConnection = ClassRegistry::init('TagConnection')->findById($tagMergeSource['tag_connection_id_2'])['TagConnection']; |
| 147 | $tagConnection['tsumego_id'] = $this->masterTsumegoID; |
| 148 | ClassRegistry::init('TagConnection')->save($tagConnection); |
| 149 | continue; |
| 150 | } |
| 151 | // when both master and slave is present, we don't have to do anything, the slave one will be removed by |
| 152 | // foreign key cascade |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | public function mergeTimeModeAttempts() |
| 157 | { |
| 158 | Util::query('UPDATE time_mode_attempt SET tsumego_id = :master_tsumego_id WHERE tsumego_id = :slave_tsumego_id', |
| 159 | [':master_tsumego_id' => $this->masterTsumegoID, ':slave_tsumego_id' => $this->slaveTsumegoID]); |
| 160 | } |
| 161 | |
| 162 | public function mergeIssues() |
| 163 | { |
| 164 | Util::query('UPDATE tsumego_issue SET tsumego_id = :master_tsumego_id WHERE tsumego_id = :slave_tsumego_id', |
| 165 | [':master_tsumego_id' => $this->masterTsumegoID, ':slave_tsumego_id' => $this->slaveTsumegoID]); |
| 166 | } |
| 167 | |
| 168 | public function execute(): array |
| 169 | { |
| 170 | if ($result = $this->checkInput()) |
| 171 | return $result; |
| 172 | |
| 173 | $db = ClassRegistry::init('Tsumego')->getDataSource(); |
| 174 | try |
| 175 | { |
| 176 | $masterSgf = ClassRegistry::init('Sgf')->find('first', [ |
| 177 | 'conditions' => ['tsumego_id' => $this->masterTsumegoID, 'accepted' => true], |
| 178 | 'order' => 'id DESC'])['Sgf']; |
| 179 | $slaveSgf = ClassRegistry::init('Sgf')->find('first', [ |
| 180 | 'conditions' => ['tsumego_id' => $this->slaveTsumegoID, 'accepted' => true], |
| 181 | 'order' => 'id DESC'])['Sgf']; |
| 182 | $oldData = [ |
| 183 | 'tsumego_old' => $this->slaveTsumegoID, |
| 184 | 'master_sgf' => $masterSgf['sgf'], |
| 185 | 'master_correct_moves' => $masterSgf['correct_moves'], |
| 186 | 'master_first_move_color' => $masterSgf['first_move_color'], |
| 187 | 'slave_sgf' => $slaveSgf['sgf'], |
| 188 | 'slave_correct_moves' => $slaveSgf['correct_moves'], |
| 189 | 'slave_first_move_color' => $slaveSgf['first_move_color']]; |
| 190 | |
| 191 | $db->begin(); |
| 192 | $this->mergeSlaveSetConnections(); |
| 193 | $this->mergeStatuses(); |
| 194 | $this->mergeTsumegoAttempts(); |
| 195 | $this->mergeComments(); |
| 196 | $this->mergeFavorites(); |
| 197 | $this->mergeTagConnections(); |
| 198 | $this->mergeTimeModeAttempts(); |
| 199 | $this->mergeIssues(); |
| 200 | ClassRegistry::init('Tsumego')->delete($this->slaveTsumegoID); |
| 201 | AdminActivityLogger::log( |
| 202 | AdminActivityType::TSUMEGO_MERGE, |
| 203 | $this->masterTsumegoID, |
| 204 | null, |
| 205 | json_encode($oldData, JSON_UNESCAPED_UNICODE)); |
| 206 | $db->commit(); |
| 207 | return ['message' => 'Tsumegos merged.', 'type' => 'success']; |
| 208 | } |
| 209 | catch (Exception $e) |
| 210 | { |
| 211 | $db->rollback(); |
| 212 | return ['message' => $e->getMessage(), 'type' => 'error']; |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | private $masterTsumegoID; |
| 217 | private $slaveTsumegoID; |
| 218 | } |