From 2c389c673c78428f9d0fa7b1b1bed98669bac1e3 Mon Sep 17 00:00:00 2001 From: "Scott J. Pearson" Date: Fri, 23 Jun 2023 16:48:33 -0500 Subject: [PATCH] 5.10.0 --- Application.php | 15 ++- CareerDev.php | 4 +- FlightTrackerExternalModule.php | 144 +++++++++++++++-------- add.php | 23 ++-- brag.php | 163 ++++++++++++++++++++------- charts/timeline.php | 31 +++-- classes/Crons.php | 7 +- classes/DataDictionaryManagement.php | 3 + classes/Download.php | 28 +++-- classes/EmailManager.php | 30 ++--- classes/Portal.php | 149 +++++++++++++++++++++++- classes/Publications.php | 35 +++--- classes/REDCapLookup.php | 17 ++- classes/Scholar.php | 2 +- classes/SocialNetworkChart.php | 5 +- classes/Upload.php | 65 ++++++++++- clean/clearBatchQueue.php | 3 +- css/career_dev.css | 1 - js/base.js | 12 ++ js/html2canvas.min.js | 8 ++ publications/getAllPubs_func.php | 30 ++--- socialNetwork/collaboration.php | 2 +- testConnectivity.php | 7 +- 23 files changed, 588 insertions(+), 196 deletions(-) create mode 100644 js/html2canvas.min.js diff --git a/Application.php b/Application.php index 0d0d61c7..cb5f6330 100644 --- a/Application.php +++ b/Application.php @@ -325,6 +325,7 @@ public static function getImportHTML() { } $str .= ""; $str .= ""; + $str .= ""; $str .= ""; return $str; } @@ -406,7 +407,7 @@ public static function getHeader($tokenName = "", $token = "", $server = "", $pi if ($base64 = $module->getBrandLogo()) { $str .= "
"; } else { - $str .= "

$tokenName

"; + $str .= "

$tokenName

"; } $str .= ""; $str .= ""; @@ -584,9 +585,12 @@ public static function getMenteeAgreementLink($pid) { echo "plugin project
"; } global $info; - if (isset($info['prod'])) { + if (isset($info['prod']) && Application::isVanderbilt()) { $sourcePid = $info['prod']['pid']; return self::link("mentor/intro.php", $sourcePid, TRUE); + } else if (isset($info['localhost']) && Application::isLocalhost()) { + $sourcePid = $info['localhost']['pid']; + return self::link("mentor/intro.php", $sourcePid, TRUE); } self::log("Warning! Could not find prod in info!"); } else if (CareerDev::isCopiedProject($pid)) { @@ -630,6 +634,13 @@ public static function getExporterFields($metadata) { return REDCapManagement::screenForFields($metadata, CareerDev::$exporterFields); } + public static function isMSTP($pid = NULL) { + if (!$pid) { + $pid = CareerDev::getPid(); + } + return Application::isVanderbilt() && ($pid == 149668); // TODO For now + } + public static function getMSTPHashFields() { $roles = self::getMSTPConfig(); $fields = []; diff --git a/CareerDev.php b/CareerDev.php index 818da45c..abd0127a 100644 --- a/CareerDev.php +++ b/CareerDev.php @@ -22,7 +22,7 @@ class CareerDev { public static $passedModule = NULL; public static function getVersion() { - return "5.9.2"; + return "5.10.0"; } public static function getLockFile($pid) { @@ -424,7 +424,7 @@ public static function getModule() { public static function getRootDir($withWebroot = FALSE) { global $server; $directoryDir = "/plugins/"; - if ($withWebroot) { + if ($withWebroot && $server) { $newWebroot = str_replace("/api/", "", $server); } else if (Application::isLocalhost()) { $newWebroot = "https://localhost/redcap"; diff --git a/FlightTrackerExternalModule.php b/FlightTrackerExternalModule.php index fee6c3cd..2b8618e2 100755 --- a/FlightTrackerExternalModule.php +++ b/FlightTrackerExternalModule.php @@ -43,6 +43,9 @@ function batch() { Application::increaseProcessingMax(8); $this->setupApplication(); $activePids = $this->getPids(); + foreach ($activePids as $pid) { + Application::log("Minute cron running", $pid); // TODO Remove - temporary + } foreach ($activePids as $pid) { # note return at end of successful run because only need to run once $token = $this->getProjectSetting("token", $pid); @@ -63,6 +66,8 @@ function batch() { \REDCap::email($adminEmail, "noreply.flighttracker@vumc.org", "Flight Tracker Batch Job Exception", $mssg); } return; + } else { + Application::log("No token or server", $pid); } } } @@ -74,6 +79,7 @@ function getPids() { function emails() { $this->setupApplication(); $activePids = $this->getPids(); + $oneHour = 3600; // CareerDev::log($this->getName()." sending emails for pids ".json_encode($pids)); foreach ($activePids as $pid) { if (REDCapManagement::isActiveProject($pid)) { @@ -85,7 +91,7 @@ function emails() { $tokenName = $this->getProjectSetting("tokenName", $pid); $adminEmail = $this->getProjectSetting("admin_email", $pid); $cronStatus = $this->getProjectSetting("send_cron_status", $pid); - if ($cronStatus) { + if ($cronStatus && (time() <= $cronStatus + $oneHour)) { $mgr = new CronManager($token, $server, $pid, $this); loadTestingCrons($mgr); $mgr->run($adminEmail, $tokenName); @@ -281,7 +287,6 @@ public function shareDataInternally($pidsSource, $pidsDest) { $tokens = []; $metadataFields = []; $choices = []; - $pidsUpdated = []; $allPids = array_unique(array_merge($pidsSource, $pidsDest)); foreach ($allPids as $pid) { @@ -339,62 +344,105 @@ public function shareDataInternally($pidsSource, $pidsDest) { } # push - foreach ($pidsSource as $i => $sourcePid) { - Application::log("Searching through pid $sourcePid", $sourcePid); - if ($tokens[$sourcePid] && $servers[$sourcePid]) { + $usedMatches = self::findMatches($pidsSource, $pidsDest, $tokens, $servers, $firstNames, $lastNames); + return $this->processMatches($usedMatches, $tokens, $servers, $forms, $metadataFields, $choices); + } + + private function processMatches($matches, $tokens, $servers, $forms, $metadataFields, $choices) { + $pidsUpdated = []; + foreach ($matches as $destLicencePlate => $sourceMatches) { + list($destPid, $destRecordId) = explode(":", $destLicencePlate); + $destToken = $tokens[$destPid]; + $destServer = $servers[$destPid]; + foreach ($sourceMatches as $sourceLicensePlate) { + list($sourcePid, $sourceRecordId) = explode(":", $sourceLicensePlate); + $sourceInfo = [ + "token" => $tokens[$sourcePid], + "server" => $servers[$sourcePid], + "pid" => $sourcePid, + "record" => $sourceRecordId, + ]; + $destInfo = [ + "token" => $destToken, + "server" => $destServer, + "pid" => $destPid, + "record" => $destRecordId, + ]; + $this->copyFormData($completes, $pidsUpdated, $forms, $sourceInfo, $destInfo, $metadataFields, $choices); + $this->copyWranglerData($pidsUpdated, $sourceInfo, $destInfo); + } + } + return $pidsUpdated; + } + + private static function findMatches($pidsSource, $pidsDest, $tokens, $servers, $firstNames, $lastNames) { + $usedMatches = []; // key is license plate of dest + $searchedFor = []; + foreach ($pidsSource as $sourcePid) { + Application::log("Searching through pid $sourcePid", $sourcePid); + if ($tokens[$sourcePid] && $servers[$sourcePid]) { $sourceToken = $tokens[$sourcePid]; $sourceServer = $servers[$sourcePid]; - foreach ($pidsDest as $i2 => $destPid) { + $sourceRecords = Download::recordIds($sourceToken, $sourceServer); + foreach ($pidsDest as $destPid) { if (($destPid != $sourcePid) && $tokens[$destPid] && $servers[$destPid]) { Application::log("Communicating between $sourcePid and $destPid", $destPid); - $destToken = $tokens[$destPid]; - $destServer = $servers[$destPid]; foreach (array_keys($firstNames[$destPid] ?? []) as $destRecordId) { - $combos = []; - foreach (NameMatcher::explodeFirstName($firstNames[$destPid][$destRecordId] ?? "") as $firstName) { - foreach (NameMatcher::explodeLastName($lastNames[$destPid][$destRecordId] ?? "") as $lastName) { - if ($firstName && $lastName) { - $combos[] = ["first" => $firstName, "last" => $lastName]; + $destCombos = self::explodeAllNames($firstNames[$destPid][$destRecordId] ?? "", $lastNames[$destPid][$destRecordId] ?? ""); + $searchedFor[] = "$destPid:$destRecordId"; + foreach ($sourceRecords as $sourceRecordId) { + if (in_array("$sourcePid:$sourceRecordId", $searchedFor)) { + # searched for other way - makes algorithm O(n*log(n)) instead of O(n^2) + if ( + isset($usedMatches["$sourcePid:$sourceRecordId"]) + && in_array("$destPid:$destRecordId", $usedMatches["$sourcePid:$sourceRecordId"]) + ) { + if (!isset($usedMatches["$destPid:$destRecordId"])) { + $usedMatches["$destPid:$destRecordId"] = []; + } + $usedMatches["$destPid:$destRecordId"][] = "$sourcePid:$sourceRecordId"; + } + } else { + $sourceCombos = self::explodeAllNames($firstNames[$sourcePid][$sourceRecordId] ?? "", $lastNames[$sourcePid][$sourceRecordId] ?? ""); + foreach ($destCombos as $destAry) { + $firstName = $destAry["first"]; + $lastName = $destAry["last"]; + Application::log("Searching for $firstName $lastName from $destPid in $sourcePid", $sourcePid); + Application::log("Searching for $firstName $lastName from $destPid in $sourcePid", $destPid); + foreach ($sourceCombos as $sourceAry) { + if (NameMatcher::matchName($firstName, $lastName, $sourceAry['first'], $sourceAry['last'])) { + Application::log("Match in above: source ($sourcePid, $sourceRecordId) to dest ($destPid, $destRecordId)", $sourcePid); + Application::log("Match in above: source ($sourcePid, $sourceRecordId) to dest ($destPid, $destRecordId)", $destPid); + if (!isset($usedMatches["$destPid:$destRecordId"])) { + $usedMatches["$destPid:$destRecordId"] = []; + } + $usedMatches["$destPid:$destRecordId"][] = "$sourcePid:$sourceRecordId"; + break; + } + } } } } - foreach ($combos as $nameAry) { - $firstName = $nameAry["first"]; - $lastName = $nameAry["last"]; - Application::log("Searching for $firstName $lastName from $destPid in $sourcePid", $sourcePid); - Application::log("Searching for $firstName $lastName from $destPid in $sourcePid", $destPid); - $originalPid = CareerDev::getPid(); - CareerDev::setPid($sourcePid); - if ($sourceRecordId = NameMatcher::matchName($firstName, $lastName, $sourceToken, $sourceServer)) { - Application::log("Match in above: source ($sourcePid, $sourceRecordId) to dest ($destPid, $destRecordId)", $sourcePid); - Application::log("Match in above: source ($sourcePid, $sourceRecordId) to dest ($destPid, $destRecordId)", $destPid); - - $sourceInfo = [ - "token" => $sourceToken, - "server" => $sourceServer, - "pid" => $sourcePid, - "record" => $sourceRecordId, - ]; - $destInfo = [ - "token" => $destToken, - "server" => $destServer, - "pid" => $destPid, - "record" => $destRecordId, - ]; - $this->copyFormData($completes, $pidsUpdated, $forms, $sourceInfo, $destInfo, $metadataFields, $choices); - $this->copyWranglerData($pidsUpdated, $sourceInfo, $destInfo); - break; // combos foreach - # if more than one match, match only first name matched - } - CareerDev::setPid($originalPid); - } } } } } } - return $pidsUpdated; - } + return $usedMatches; + } + + private static function explodeAllNames($lastName, $firstName) { + $combos = []; + foreach (NameMatcher::explodeFirstName($firstName) as $firstName) { + foreach (NameMatcher::explodeLastName($lastName) as $lastName) { + if ($firstName && $lastName) { + $combos[] = ["first" => $firstName, "last" => $lastName]; + } + } + } + return $combos; + } + public function copyWranglerData(&$pidsUpdated, $sourceInfo, $destInfo) { $hasCopiedPubData = $this->copyPubData($sourceInfo, $destInfo); @@ -653,7 +701,11 @@ private function processInstrument($instrument, $forms, $completeData, &$destDat if ($config['formType'] == "repeating") { $canGo = FALSE; - $dataValues = array_values($completeData[$sourcePid][$sourceRecordId] ?: []); + if (is_array($completeData[$sourcePid][$sourceRecordId] ?: [])) { + $dataValues = array_values($completeData[$sourcePid][$sourceRecordId] ?: []); + } else { + $dataValues = [$completeData[$sourcePid][$sourceRecordId]]; + } foreach ($markedAsComplete as $completeValue) { if (in_array($completeValue, $dataValues)) { $canGo = TRUE; diff --git a/add.php b/add.php index 826e0a62..f783614c 100644 --- a/add.php +++ b/add.php @@ -10,6 +10,7 @@ use \Vanderbilt\CareerDevLibrary\REDCapLookup; use \Vanderbilt\CareerDevLibrary\NIHTables; + require_once(dirname(__FILE__)."/charts/baseWeb.php"); require_once(dirname(__FILE__)."/classes/Autoload.php"); @@ -69,25 +70,25 @@ } } else if ($_POST['action'] == "importText") { $list = REDCapManagement::sanitize($_POST['newnames']); - $rows = explode("\n", $list); - foreach ($rows as $row) { - if ($row) { - $nodes = preg_split("/\s*[,\t]\s*/", $row); - if (count($nodes) == 6) { - $lines[] = $nodes; - } else { + $rows = explode("\n", $list); + foreach ($rows as $row) { + if ($row) { + $nodes = preg_split("/\s*[,\t]\s*/", $row); + if (count($nodes) == 6) { + $lines[] = $nodes; + } else { $link = Application::link("this").$createRecordsURI; $mssg = "A line does not contain the necessary 6 columns. No data have been added. Please try again."; exitProcess($mssg, $link); exit; - } - } - } + } + } + } } else { throw new \Exception("This should never happen."); } $mentorUids = getUidsForMentors($lines); - if (!empty($mentorUids) || Application::isLocalhost()) { + if (!empty($mentorUids)) { echo makeAdjudicationTable($lines, $mentorUids, [], []); $url = APP_PATH_WEBROOT."ProjectGeneral/keep_alive.php?pid=".$pid; echo "