Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for Merge Duplicates Failing when no Determinations need Downgrading #1484

Merged
Merged
143 changes: 130 additions & 13 deletions classes/OccurrenceEditorManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,97 @@ private function getIdentifiers($occidStr){
return $retArr;
}

private function addLatestIdentToDetermination($occid) : void {
//If determination is already in omoccurdeterminations, INSERT will fail
$guid = UuidFactory::getUuidV4();
$sqlInsert = 'INSERT IGNORE INTO omoccurdeterminations(occid, identifiedBy, dateIdentified, sciname, scientificNameAuthorship, '.
'identificationQualifier, identificationReferences, identificationRemarks, recordID, sortsequence, isCurrent) '.
'SELECT occid, IFNULL(identifiedby,"unknown") AS idby, IFNULL(dateidentified,"s.d.") AS di, '.
'sciname, scientificnameauthorship, identificationqualifier, identificationreferences, identificationremarks, "'.$guid.'", 10 AS sortseq, (SELECT IF(COUNT(*) > 0, 0, 1) AS isCur from omoccurdeterminations where isCurrent = 1 and occid = '. $occid . ') '.
'FROM omoccurrences WHERE (occid = ' . $occid . ') AND (identifiedBy IS NOT NULL OR dateIdentified IS NOT NULL OR sciname IS NOT NULL)';
try {
$this->conn->query($sqlInsert);
} catch (mysqli_sql_exception $e) {
echo 'Duplicate: '.$this->conn->error;
error_log('Error Duplicate determination from latest identification:' . $e->getMessage());
}
}

// Function is only exists to move otherCatalogNumber when merging records
// This should be removed when otherCatalogNumber is no longer in omoccurrences
private function addLegacyIdentifers($occid) : void {
$sql_cnt = 'SELECT COUNT(*) AS cnt FROM omoccuridentifiers oi join omoccurrences o on o.occid = oi.occid WHERE o.occid = ?';
$sql_insert = 'INSERT INTO omoccuridentifiers(occid, identifierName, identifierValue, notes, modifiedUid) select occid,"legacyOtherCatalogNumber" as identifierName, otherCatalogNumbers as identifierValue, "Auto generated during record merge" as notes, ? as modifiedUid from omoccurrences where occid = ?';
try {
$result_cnt = $this->conn->execute_query($sql_cnt, [$occid]);
$cnt = ($result_cnt->fetch_assoc())["cnt"];
if($cnt === 0) {
$this->conn->execute_query($sql_insert,[$GLOBALS['SYMB_UID'], $occid]);
}
} catch (mysqli_sql_exception $e) {
error_log('Error: Failed to add otherCatalogNumbers to omoccuridentifiers for occid '. $occid . ' :' . $e->getMessage());
}
}

// Copy of updateBaseOccurrence in OccurrenceEditorDeterminations for temporary utility till 3.2
// TODO (Logan) remove once latest Identification section in editor is removed
private function updateBaseOccurrence($detId){
if(is_numeric($detId)){
$taxonArr = $this->getTaxonVariables($detId);
$sql = 'UPDATE omoccurrences o INNER JOIN omoccurdeterminations d ON o.occid = d.occid
SET o.identifiedBy = d.identifiedBy, o.dateIdentified = d.dateIdentified, o.sciname = d.sciname, o.scientificNameAuthorship = d.scientificnameauthorship,
o.identificationQualifier = d.identificationqualifier, o.identificationReferences = d.identificationReferences, o.identificationRemarks = d.identificationRemarks,
o.taxonRemarks = d.taxonRemarks, o.genus = NULL, o.specificEpithet = NULL, o.taxonRank = NULL, o.infraspecificepithet = NULL, o.scientificname = NULL ';
if(isset($taxonArr['family']) && $taxonArr['family']) $sql .= ', o.family = "'.$this->cleanInStr($taxonArr['family']).'"';
if(isset($taxonArr['tid']) && $taxonArr['tid']) $sql .= ', o.tidinterpreted = '.$taxonArr['tid'];
if(isset($taxonArr['security']) && $taxonArr['security']) $sql .= ', o.localitysecurity = '.$taxonArr['security'].', o.localitysecurityreason = "<Security Setting Locked>"';
$sql .= ' WHERE (d.iscurrent = 1) AND (d.detid = '.$detId.')';
$updated_base = $this->conn->query($sql);

//Whenever occurrence is updated also update associated images
if($updated_base && isset($taxonArr['tid']) && $taxonArr['tid']) {
$sql = <<<'SQL'
UPDATE images i
INNER JOIN omoccurdeterminations od on od.occid = i.occid
SET tid = ? WHERE detid = ?;
SQL;
$this->conn->execute_query($sql, [$taxonArr['tid'], $detId]);
}
}
}

// Copy of getTaxonVariables in OccurrenceEditorDeterminations for temporary utility till 3.2
// TODO (Logan) remove once latest Identification section in editor is removed
private function getTaxonVariables($detId){
$retArr = array();
$sqlTid = 'SELECT t.tid, t.securitystatus, ts.family
FROM omoccurdeterminations d INNER JOIN taxa t ON d.sciname = t.sciname
INNER JOIN taxstatus ts ON t.tid = ts.tid
WHERE (d.detid = '.$detId.') AND (taxauthid = 1)';
$rs = $this->conn->query($sqlTid);
if($r = $rs->fetch_object()){
$retArr['tid'] = $r->tid;
$retArr['family'] = $r->family;
$retArr['security'] = ($r->securitystatus == 1 ? 1 : 0);
}
$rs->free();
if($retArr && !$retArr['security'] && $retArr['tid']){
$sql2 = 'SELECT c.clid
FROM fmchecklists c INNER JOIN fmchklsttaxalink cl ON c.clid = cl.clid
INNER JOIN taxstatus ts1 ON cl.tid = ts1.tid
INNER JOIN taxstatus ts2 ON ts1.tidaccepted = ts2.tidaccepted
INNER JOIN omoccurrences o ON c.locality = o.stateprovince
WHERE c.type = "rarespp" AND ts1.taxauthid = 1 AND ts2.taxauthid = 1
AND (ts2.tid = '.$retArr['tid'].') AND (o.occid = '.$this->occid.')';
$rs2 = $this->conn->query($sql2);
if($rs2->num_rows){
$retArr['security'] = 1;
}
$rs2->free();
}
return $retArr;
}

public function addOccurrence($postArr){
global $LANG;
$status = $LANG['SUCCESS_NEW_OCC_SUBMITTED'];
Expand All @@ -1186,6 +1277,7 @@ public function addOccurrence($postArr){
}
else $sql .= ', NULL';
}

$sql .= ')';
if($this->conn->query($sql)){
$this->occid = $this->conn->insert_id;
Expand Down Expand Up @@ -1268,6 +1360,9 @@ public function addOccurrence($postArr){
$status = $LANG['FAILED_ADD_OCC'].": ".$this->conn->error.'<br/>SQL: '.$sql;
}
}

$this->addLatestIdentToDetermination($this->occid);

return $status;
}

Expand Down Expand Up @@ -1514,6 +1609,7 @@ public function cloneOccurrence($postArr){
return $retArr;
}

// Note source is record that started duplicate lookup and is deleted up success
public function mergeRecords($targetOccid,$sourceOccid){
global $LANG;
$status = true;
Expand All @@ -1529,6 +1625,14 @@ public function mergeRecords($targetOccid,$sourceOccid){
/* Start transaction */
// This will autocommit if not rollback explicitly
$this->conn->begin_transaction();

//Add Latest Determination if missing
$this->addLatestIdentToDetermination($targetOccid);
$this->addLatestIdentToDetermination($sourceOccid);

//Add otherCatalogNumbers to Identifiers if missing
$this->addLegacyIdentifers($targetOccid);
$this->addLegacyIdentifers($sourceOccid);
$stage = '';
try {
$oArr = array();
Expand Down Expand Up @@ -1561,12 +1665,17 @@ public function mergeRecords($targetOccid,$sourceOccid){
$this->conn->execute_query($sqlIns, [$targetOccid]);
}

// Anon function for util of merging determinations
$get_current_determinations = function ($occid) {
$sql =<<<'SQL'
SELECT detid FROM omoccurdeterminations where occid = ? and isCurrent = 1;
SQL;
$result = $this->conn->execute_query($sql, [$occid]);
return array_map(fn($v) => $v[0], $result->fetch_all());
};

//Fetch List of Old Current Determinations
$sql =<<<'SQL'
SELECT detid FROM omoccurdeterminations where occid = ? and isCurrent = 1;
SQL;
$result = $this->conn->execute_query($sql, [$targetOccid]);
$currentDeterminations = array_map(fn($v) => $v[0], $result->fetch_all());
$currentDeterminations = $get_current_determinations($targetOccid);

//Remap determinations
$sql = <<<'SQL'
Expand All @@ -1593,14 +1702,22 @@ public function mergeRecords($targetOccid,$sourceOccid){
]);

//Downgrade old determinations if new determinations have a current determination
$parameters = str_repeat('?,', count($currentDeterminations) - 1) . '?';
$sql = <<<"SQL"
UPDATE omoccurdeterminations
JOIN (SELECT count(*) as cnt FROM omoccurdeterminations WHERE isCurrent = 1 AND occid = ? AND detid NOT IN ($parameters)) as update_flag on cnt > 0
SET isCurrent = 0
WHERE occid = ? AND isCurrent = 1 AND detid IN ($parameters);
SQL;
$this->conn->execute_query($sql, array_merge([$targetOccid], $currentDeterminations, [$targetOccid], $currentDeterminations));
if(count($currentDeterminations) > 0) {

$parameters = str_repeat('?,', count($currentDeterminations) - 1) . '?';
$sql = <<<"SQL"
UPDATE omoccurdeterminations
SET isCurrent = 0
WHERE occid = ? AND isCurrent = 1 AND detid NOT IN ($parameters);
SQL;
$this->conn->execute_query($sql, array_merge([$targetOccid], $currentDeterminations));
}

// Get New Current determination and updateBaseOccurrence to match
$currentDeterminations = $get_current_determinations($targetOccid);
if(is_array($currentDeterminations) && count($currentDeterminations) > 0) {
$this->updateBaseOccurrence($currentDeterminations[0]);
}

//Remap images
$sql = <<<'SQL'
Expand Down