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 .= "";
+ $str .= "";
}
$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 "