diff --git a/Application.php b/Application.php index ea8c3585..1b94a05c 100644 --- a/Application.php +++ b/Application.php @@ -3,8 +3,10 @@ namespace Vanderbilt\CareerDevLibrary; use \Vanderbilt\FlightTrackerExternalModule\CareerDev; +use \Vanderbilt\FlightTrackerExternalModule\FlightTrackerExternalModule; require_once(dirname(__FILE__)."/CareerDev.php"); +require_once(dirname(__FILE__)."/FlightTrackerExternalModule.php"); class Application { public static function getVersion() { @@ -68,7 +70,7 @@ public static function has($instrument, $pid = "") { } public static function getFlightConnectorURL() { - return "https://redcap.vanderbilt.edu/external_modules/?prefix=flight_connector&page=map&pid=172928&h=5986967536b44df5&NOAUTH"; + return "https://redcap.vumc.org/external_modules/?prefix=flight_connector&page=map&pid=172928&h=5986967536b44df5&NOAUTH"; } public static function getDataTable($pid) { @@ -292,7 +294,7 @@ public static function isPluginProject($project_id = "") { (self::isVanderbilt() && ($project_id == NEWMAN_SOCIETY_PROJECT)) || (self::isVanderbilt() && $isCopiedPluginProject) || (self::isLocalhost() && ($project_id == LOCALHOST_TEST_PROJECT)) - || (self::isServer("redcaptest.vanderbilt.edu") && ($project_id == REDCAPTEST_TEST_PROJECT)) + || (self::isServer(FlightTrackerExternalModule::VANDERBILT_TEST_SERVER) && ($project_id == REDCAPTEST_TEST_PROJECT)) ); } @@ -396,7 +398,7 @@ public static function getHeader($tokenName = "", $token = "", $server = "", $pi $str .= "
"; $str .= "
"; - $str .= "
Flight Tracker for Scholars
"; + $str .= "
Flight Tracker for Scholars
"; # logged in and on a record if (Application::getUsername() && (isset($_GET['id']) || isset($_GET['record']))) { $records = Download::records($token, $server); @@ -589,9 +591,9 @@ public static function getPids() { } if (self::isLocalhost()) { array_unshift($pids, LOCALHOST_TEST_PROJECT); - } else if (self::isServer("redcap.vanderbilt.edu")) { + } else if (self::isServer("redcap.vanderbilt.edu") || self::isServer("redcap.vumc.org")) { array_unshift($pids, NEWMAN_SOCIETY_PROJECT); - } else if (self::isServer("redcaptest.vanderbilt.edu")) { + } else if (self::isServer(FlightTrackerExternalModule::VANDERBILT_TEST_SERVER)) { # TODO Add test projects with plugin } } else { @@ -906,8 +908,8 @@ public static function getScholarPortalLink() { return self::link("portal/index.php"); } - public static function applySecurityHeaders() { - $serverList = Application::getSetting("safe_servers"); + public static function applySecurityHeaders($pid) { + $serverList = Application::getSetting("safe_servers", $pid); if (!$serverList) { return; } diff --git a/CareerDev.php b/CareerDev.php index 95b76791..5ef675ee 100644 --- a/CareerDev.php +++ b/CareerDev.php @@ -22,7 +22,7 @@ class CareerDev { public static $passedModule = NULL; public static function getVersion() { - return "6.10.0"; + return "6.10.1"; } public static function getLocalhostPluginPid() { @@ -125,7 +125,7 @@ public static function getSites($all = TRUE) { "PubMed Central Converter" => "www.ncbi.nlm.nih.gov", "iCite" => "icite.od.nih.gov", "ORCID" => "pub.orcid.org", - "Statistics Reporting" => "redcap.vanderbilt.edu", + "Statistics Reporting" => "redcap.vumc.org", "Altmetric" => "api.altmetric.com", "Patents View (US Patent Office)" => "api.patentsview.org", "NSF Grants" => "api.nsf.gov", @@ -307,13 +307,28 @@ public static function unsetPid() { self::$pid = NULL; } - private static function constructThisURL() { + private static function getProtocol($uri) { $isHTTPS = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')); $serverPort = $_SERVER['SERVER_PORT'] ?? 0; $isSSLPort = $serverPort == 443; - $protocol = ($isHTTPS || $isSSLPort) ? "https://" : "http://"; - $host = $_SERVER['HTTP_HOST'] ?? ""; + if ($isHTTPS || $isSSLPort) { + return "https://"; + } else if (isset($_GET['pid'])) { + $myserver = self::getSetting("server", Sanitizer::sanitizePid($_GET['pid'])); + if (preg_match("/^https:/i", $myserver)) { + # assume https because the setup server requests it + # this facilitates unusual REDCap cloud setups where SSL is enabled for the URL + # but the apache server just uses http because a proxy handles the encryption + return "https://"; + } + } + return "http://"; + } + + private static function constructThisURL() { $uri = $_SERVER['REQUEST_URI'] ?? ""; + $protocol = self::getProtocol($uri); + $host = $_SERVER['HTTP_HOST'] ?? ""; return $protocol.$host.$uri; } @@ -446,7 +461,7 @@ public static function parseGetParams($url) { } public static function makeLogo() { - return "Flight Tracker for Scholars"; + return "Flight Tracker for Scholars"; } public static function link($relativeUrl, $pid = "", $withWebroot = FALSE) { @@ -499,10 +514,10 @@ public static function getRootDir($withWebroot = FALSE) { private static function getThisPageURL($project_id, $isPortalPage) { if (Application::isPluginProject($project_id)) { - $port = $_SERVER['SERVER_PORT'] ?? ""; - $protocol = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || ($port == 443)) ? "https://" : "http://"; + $uri = $_SERVER['REQUEST_URI'] ?? ""; + $protocol = self::getProtocol($uri); $host = $_SERVER['HTTP_HOST'] ?? ""; - $fileLocation = explode("?", $_SERVER['REQUEST_URI'] ?? "")[0]; + $fileLocation = explode("?", $uri)[0]; $url = $protocol . $host . $fileLocation; if ($project_id) { $url .= "?pid=".urlencode($project_id); @@ -600,7 +615,7 @@ public static function getModulePageURL($http, $project_id, $isMentorAgreementPa private static function getREDCapSignupURL() { if (self::isVanderbilt()) { - return 'https://redcap.vanderbilt.edu/surveys/?s=L4R3NJ8XME'; + return 'https://redcap.vumc.org/surveys/?s=L4R3NJ8XME'; } else { global $homepage_contact_url, $homepage_contact_email; if ($homepage_contact_url) { @@ -727,7 +742,7 @@ public static function getPidFromToken($localToken = "") { return $pid; } } - if ($localToken == $token) { + if ($pid && ($localToken == $token)) { return $pid; } foreach ($info as $key => $row) { @@ -881,10 +896,10 @@ public static function getAllSettings($pid = "") { public static function getSetting($field, $pid = "") { if ( self::isVanderbilt() - && ((Application::isServer("redcap.vanderbilt.edu") && ($pid == NEWMAN_SOCIETY_PROJECT)) + && ((Application::isServer("redcap.vumc.org") && ($pid == NEWMAN_SOCIETY_PROJECT)) || ((Application::isLocalhost() && ($pid == LOCALHOST_TEST_PROJECT)))) ) { - # TODO add redcaptest.vanderbilt.edu + # TODO add redcaptest.vumc.org $module = ExternalModules::getModuleInstance("vanderbilt_plugin-settings"); $items = [$field]; if (!preg_match("/\/plugins\//", $_SERVER['REQUEST_URI'] ?? "")) { @@ -1037,7 +1052,7 @@ public static function isVanderbilt() { if (Application::isLocalhost()) { return TRUE; } - return preg_match("/vanderbilt.edu/", SERVER_NAME); + return preg_match("/vanderbilt.edu/", SERVER_NAME) || preg_match("/vumc.org/", SERVER_NAME); } public static function getRepeatingFormsAndLabels($metadata = []) { @@ -1091,7 +1106,11 @@ public static function isLocalhost() { } public static function isTestGroup($pid) { - return (SERVER_NAME == "redcap.vanderbilt.edu") && in_array($pid, [105963, 101785]); + return ( + Application::isServer("redcap.vanderbilt.edu") + || Application::isServer("redcap.vumc.org") + ) + && in_array($pid, [105963, 101785]); } public static function duplicateAllSettings($srcPid, $destPid, $defaultSettings = []) { @@ -1222,7 +1241,7 @@ public static function getMenu($menuName, $pid = NULL) { $ary["Import General Data"] = self::link("/import.php"); $ary["Import Positions"] = self::link("/bulkImport.php") . "&positions"; if (self::isVanderbilt() && ($pid == 145767)) { - $ary["LDAP Lookup"] = "https://redcap.vanderbilt.edu/plugins/career_dev/LDAP/ldapLookup.php"; + $ary["LDAP Lookup"] = "https://redcap.vumc.org/plugins/career_dev/LDAP/ldapLookup.php"; } return $ary; diff --git a/CareerDevHelp.php b/CareerDevHelp.php index 54b6a567..5b17eeb1 100644 --- a/CareerDevHelp.php +++ b/CareerDevHelp.php @@ -19,7 +19,7 @@ public static function getHelp($title, $menu) { } public static function getVideoVaultLinkHTML() { - $url = "https://redcap.vanderbilt.edu/plugins/career_dev/help/videos.php"; + $url = "https://redcap.vumc.org/plugins/career_dev/help/videos.php"; $videosHTML = Links::makeLink($url, "Flight Tracker Video Vault", TRUE); return "

$videosHTML

"; } diff --git a/FlightTrackerExternalModule.php b/FlightTrackerExternalModule.php index 60c928b5..fdd46fd6 100755 --- a/FlightTrackerExternalModule.php +++ b/FlightTrackerExternalModule.php @@ -37,6 +37,7 @@ class FlightTrackerExternalModule extends AbstractExternalModule const OLD_LAST_NAMES = "old_last_names"; const FIRST_NAMES = "first_names"; const LAST_NAMES = "last_names"; + const VANDERBILT_TEST_SERVER = "redcaptest.vumc.org"; function getPrefix() { return Application::getPrefix(); diff --git a/bulkImport.php b/bulkImport.php index 19749425..ab412566 100644 --- a/bulkImport.php +++ b/bulkImport.php @@ -154,15 +154,19 @@ } echo "
\n"; } else { - try { - $feedback = Upload::rows($upload, $token, $server); - if ($feedback['error']) { - echo "

ERROR! ".$feedback['error']."

\n"; - } else { - echo "

Upload successful!

\n"; + if (!empty($upload)) { + try { + $feedback = Upload::rows($upload, $token, $server); + if (is_array($feedback) && $feedback['error']) { + echo "

ERROR! ".$feedback['error']."

"; + } else { + echo "

Upload successful!

"; + } + } catch (\Exception $e) { + echo "

ERROR! ".$e->getMessage()."

"; } - } catch (\Exception $e) { - echo "

ERROR! ".$e->getMessage()."

\n"; + } else { + echo "

ERROR! No data have been matched.

"; } } } else { diff --git a/classes/CelebrationsEmail.php b/classes/CelebrationsEmail.php index e628af88..ad7c0987 100644 --- a/classes/CelebrationsEmail.php +++ b/classes/CelebrationsEmail.php @@ -259,7 +259,7 @@ private function processCitations($redcapData, $warningTs, $currBios, $requested $journalHandles[] = Sanitizer::sanitizeWithoutChangingQuotes($journalRow['handle']); } } - $journalHTML = empty($journalHandles) ? $journalHTML." (add new journal handle?)" : $journalHTML." (".implode(", ", $journalHandles).")"; + $journalHTML = empty($journalHandles) ? $journalHTML." (add new journal handle?)" : $journalHTML." (".implode(", ", $journalHandles).")"; } $journalHTML .= ""; foreach ($pmidsIdentified[$pmid] as $matchRecordId) { diff --git a/classes/DataDictionaryManagement.php b/classes/DataDictionaryManagement.php index 58cfa91f..b73e4c1d 100644 --- a/classes/DataDictionaryManagement.php +++ b/classes/DataDictionaryManagement.php @@ -1221,7 +1221,7 @@ public static function mergeMetadataAndUploadNew($originalMetadata, $fileMetadat } public static function updateAlumniAssociations(&$metadata, $originalMetadata, $pid) { - $alumniURL = "https://redcap.vanderbilt.edu/plugins/career_dev/getAlumniAssociations.php?NOAUTH"; + $alumniURL = "https://redcap.vumc.org/plugins/career_dev/getAlumniAssociations.php?NOAUTH"; list($resp, $alumniJSON) = URLManagement::downloadURL($alumniURL, $pid); $indexedOriginalMetadata = self::indexMetadata($originalMetadata); $nativeAlumniAssociations = json_decode($alumniJSON ?: "[]", TRUE); diff --git a/classes/DateManagement.php b/classes/DateManagement.php index d1212817..f751a1b9 100644 --- a/classes/DateManagement.php +++ b/classes/DateManagement.php @@ -7,6 +7,21 @@ class DateManagement { const SEP_REGEX = "[\/\-]"; + public static function getEarliestDate($dates) { + if (count($dates) == 0) { + return ""; + } else if (count($dates) == 1) { + return $dates[0]; + } + $earliestDate = $dates[0]; + for ($i = 1; $i < count($dates); $i++) { + $date = $dates[$i]; + if (DateManagement::dateCompare($date, "<", $earliestDate)) { + $earliestDate = $date; + } + } + return $earliestDate; + } public static function getFederalFiscalYear($ts = FALSE) { if (!$ts) { $ts = time(); @@ -47,7 +62,7 @@ public static function getWeekNumInYear($ts = FALSE) { $dt = new \DateTime(); $dt->setTimestamp($ts); $dayOfYear = $dt->format("z"); - return floor(($dayOfYear - 1) / 7) + 1; + return floor(((int) $dayOfYear - 1) / 7) + 1; } public static function getWeekNumInMonth($ts = FALSE) { @@ -57,7 +72,7 @@ public static function getWeekNumInMonth($ts = FALSE) { $dt = new \DateTime(); $dt->setTimestamp($ts); $dayOfMonth = $dt->format("j"); - return floor(($dayOfMonth - 1) / 7) + 1; + return floor(((int) $dayOfMonth - 1) / 7) + 1; } public static function isYear($d) { @@ -168,9 +183,9 @@ public static function getYearDuration($date1, $date2) { } public static function REDCapTsToPHPTs($redcapTs) { - $year = substr($redcapTs, 0, 4); - $month = substr($redcapTs, 4, 2); - $day = substr($redcapTs, 6, 2); + $year = (int) substr($redcapTs, 0, 4); + $month = (int) substr($redcapTs, 4, 2); + $day = (int) substr($redcapTs, 6, 2); $dt = new \DateTime(); $dt->setDate($year, $month, $day); return $dt->getTimestamp(); diff --git a/classes/Definitions.php b/classes/Definitions.php index f1ef96bf..7b234525 100644 --- a/classes/Definitions.php +++ b/classes/Definitions.php @@ -99,7 +99,7 @@ public function getHTML() { } public function getCodeBookLink() { - return "Download the Project's CodeBook ".Links::makeLink("https://redcap.vanderbilt.edu/plugins/career_dev/docs/Codebook.docx", "here")."."; + return "Download the Project's CodeBook ".Links::makeLink("https://redcap.vumc.org/plugins/career_dev/docs/Codebook.docx", "here")."."; } public function getSubText() { diff --git a/classes/Download.php b/classes/Download.php index 6e5f7491..70948d33 100644 --- a/classes/Download.php +++ b/classes/Download.php @@ -505,6 +505,49 @@ private static function getCachedMetadata($pid, $fields) { return []; } + # Cf. API/project/export.php getItems() + public static function projectSettingsByPid($pid) { + # keys are values from redcap_projects table; values are user-facing names + require_once(APP_PATH_DOCROOT."Classes/Project.php"); + require_once(APP_PATH_DOCROOT."Config/init_functions.php"); + $projectFields = \Project::getAttributesApiExportProjectInfo(); + $module = Application::getModule(); + + $values = []; + # Use * for column names because of the way that \Project::getAttributesApiExportProjectInfo() gets data + # This is generally bad practice, agreed, but it is the best way to handle new columns in this instance + $sql = "SELECT * FROM redcap_projects WHERE project_id = ?"; + $result = $module->query($sql, [$pid]); + if ($row = $result->fetch_assoc()) { + foreach ($projectFields as $dbKey => $userKey) { + $val = ""; + if (isset($row[$dbKey])) { + $val = $row[$dbKey]; + if (is_bool($val)) { + $val = ($val === FALSE) ? 0 : 1; + } else { + # REDCap's limited set of html special chars, rather than html_entity_decode + $val = \label_decode($val); + } + } + $values[$userKey] = \isinteger($val) ? (int) $val : $val; + } + } + $values["is_longitudinal"] = 0; // always classical + $values["has_repeating_instruments_or_events"] = 1; + $versionsByPrefix = \ExternalModules\ExternalModules::getEnabledModules($pid); + $values["external_modules"] = implode(",", array_keys($versionsByPrefix)); + + # From REDCap: Reformat the missing data codes to be pipe-separated + $theseMissingCodes = array(); + foreach (\parseEnum($values['missing_data_codes'] ?? "") as $key=>$val) { + $theseMissingCodes[] = "$key, $val"; + } + $values['missing_data_codes'] = implode(" | ", $theseMissingCodes); + + return $values; + } + public static function metadataByPid($pid, $fields = []) { if (isset($_GET['test']) || isset($_GET['testPSU'])) { Application::log("Download::metadataByPid", $pid); @@ -1512,7 +1555,7 @@ public function downloads_test($tester) { $records = array("1"); $fields = array("record_id"); $token = "C65F37B496A52AE5E044A8D79FDD2A02"; - $server = "https://redcap.vanderbilt.edu/api/"; + $server = "https://redcap.vumc.org/api/"; $tester->tag("fieldsForRecords"); $ary = Download::fieldsForRecords($token, $server, $fields, $records); @@ -1727,10 +1770,19 @@ public static function fieldsForRecords($token, $server, $fields, $records) { return self::sendToServer($server, $data); } + public static function recordsByPid($pid, $records = NULL) { + if (!isset($records)) { + # wrong function => re-route + return self::recordIdsByPid($pid); + } + $fields = NULL; + return self::fieldsForRecordsByPid($pid, $fields, $records); + } + public static function records($token, $server, $records = NULL) { if (!isset($records)) { # assume recordIds was meant if $records null - return Download::recordIds($token, $server); + return self::recordIds($token, $server); } if (isset($_GET['test'])) { Application::log("Download::records ".json_encode($records)); diff --git a/classes/Links.php b/classes/Links.php index 65528e54..19591a86 100644 --- a/classes/Links.php +++ b/classes/Links.php @@ -89,7 +89,7 @@ public static function makeLink($url, $text, $launchNewWindow = FALSE, $linkClas } public function makeLink_test($tester) { - $url = "https://redcap.vanderbilt.edu/plugins/career_dev/index.php"; + $url = "https://redcap.vumc.org/plugins/career_dev/index.php"; $text = "Test Link"; $link = self::makeLink($url, $text); $tester->assertMatch("/$text/", $link); diff --git a/classes/NIHTables.php b/classes/NIHTables.php index c5cc5ff9..e553573a 100644 --- a/classes/NIHTables.php +++ b/classes/NIHTables.php @@ -1112,7 +1112,7 @@ public static function separateRecordAndHeaders($data, $title, $tableNum) { $newRow['pid'] = $pid; $newRow['record'] = $recordId; $newRow['recordInstance'] = $recordInstance; - $newRow['email'] = implode(";", $emailsWithName); + $newRow['email'] = implode(";", array_filter($emailsWithName)); for ($j = 0; $j < count($row); $j++) { $newRow[$headers[$j]] = $row[$j]; } @@ -1712,7 +1712,7 @@ private static function getEarliestStartDate($redcapData, $recordId) { } if ($dateUnderConsideration) { $startTs = strtotime($dateUnderConsideration); - if (!$ts || ($ts < $startTs)) { + if (!$ts || ($ts > $startTs)) { $ts = $startTs; } } @@ -2191,6 +2191,7 @@ private function getResearchTopic($recordId) { $validGrantClasses = array("K", "T", "Other", ""); } + $initialResearchTopic = $this->getInitialResearchTopic($recordId); if ($grantClass == "K") { # if K => K Title $fields = array_unique(array_merge( @@ -2208,18 +2209,23 @@ private function getResearchTopic($recordId) { } } } + if ($initialResearchTopic) { + return $initialResearchTopic; + } if (!$lastValidKTitle) { return $this->getCustomGrantTitle($recordId, [1, 2, 10]); } return $lastValidKTitle; } else if ($grantClass == "T") { # if T => from survey (check_degree0_topic or custom_title) - $topics = Download::oneField($this->token, $this->server, "check_degree0_topic"); - if ($topics[$recordId]) { - return $topics[$recordId]; + if ($initialResearchTopic) { + return $initialResearchTopic; } return $this->getCustomGrantTitle($recordId, self::getTrainingTypesForGrantClass()); } else if (($grantClass == "Other") || ($grantClass == "")) { + if ($initialResearchTopic) { + return $initialResearchTopic; + } # if other => blank return ""; } else if (in_array($grantClass, $validGrantClasses)) { @@ -2228,6 +2234,18 @@ private function getResearchTopic($recordId) { throw new \Exception("Your grant class ($grantClass) is invalid!"); } } + + private function getInitialResearchTopic($recordId) { + $surveyTopic = Download::oneFieldForRecordByPid($this->pid, "check_degree0_topic", $recordId); + if ($surveyTopic) { + return $surveyTopic; + } + $importTopic = Download::oneFieldForRecordByPid($this->pid, "init_import_degree0_topic", $recordId); + if ($importTopic) { + return $importTopic; + } + return ""; + } private function getAlteredChoices() { if (!$this->choices) { @@ -2731,11 +2749,14 @@ public function get8Data($table, $records = []) { foreach ($names as $recordId => $name) { $currentTrainingGrants = self::getTrainingGrantsForRecord($trainingGrants, $recordId); - $startDate = self::getEarliestStartDate($currentTrainingGrants, $recordId); - if (!$startDate) { + $customGrantStartDate = self::getEarliestStartDate($currentTrainingGrants, $recordId); + $initSurveyStartDate = Download::oneFieldForRecordByPid($this->pid, "check_degree0_start", $recordId); + $initImportStartDate = Download::oneFieldForRecordByPid($this->pid, "check_degree0_start", $recordId); + if (!$customGrantStartDate && !$initSurveyStartDate && !$initImportStartDate) { $countingStartDate = $baseLineStart; } else { - $countingStartDate = $startDate; + $eligibleDates = REDCapManagement::removeBlanksFromAry([$customGrantStartDate, $initSurveyStartDate, $initImportStartDate]); + $countingStartDate = DateManagement::getEarliestDate($eligibleDates); } $startingDates[$recordId] = strtotime($countingStartDate); } diff --git a/classes/ORCID.php b/classes/ORCID.php index 77fb79a2..099eb377 100644 --- a/classes/ORCID.php +++ b/classes/ORCID.php @@ -142,7 +142,9 @@ private static function makeQueryFromParams($params) { if (is_array($value)) { $values = array(); foreach ($value as $v) { - $values[] = $v; + if ($v) { + $values[] = $v; + } } if (count($values) > 1) { $queryStrings[] = $key . ":" . "(" . implode(" OR ", $values) . ")"; diff --git a/classes/Portal.php b/classes/Portal.php index 430b877c..d29886f0 100644 --- a/classes/Portal.php +++ b/classes/Portal.php @@ -612,7 +612,7 @@ public function getStoredData() { } } $this->removeDisassociations($storedData['matches']); - if (Application::isVanderbilt() && Application::isServer("redcap.vanderbilt.edu")) { + if (Application::isVanderbilt() && (Application::isServer("redcap.vanderbilt.edu") || Application::isServer("redcap.vumc.org"))) { self::filterOutNonNewmanProject($storedData['matches']); } return $storedData; diff --git a/classes/Publications.php b/classes/Publications.php index 0d1f6ffd..93d93485 100644 --- a/classes/Publications.php +++ b/classes/Publications.php @@ -932,12 +932,22 @@ public static function deleteMismatchedRows($token, $server, $pid, $recordId, $a $foundCurrInAuthorList = FALSE; $foundAnotherInAuthorList = FALSE; foreach ($authors as $author) { - if ( - NameMatcher::matchByInitials($currLastName, $currFirstName, $author['last'], $author['first']) - || NameMatcher::matchByInitials($currFirstName, $currLastName, $author['last'], $author['first']) // reverse for Asian names which sometimes put family name first - ) { - $foundCurrInAuthorList = TRUE; - // Application::log("Found current $currFirstName $currLastName {$author['last']}, {$author['first']} in author list in $recordId:$instance $pmid", $pid); + foreach (NameMatcher::explodeLastName($currLastName) as $currLN) { + foreach (NameMatcher::explodeFirstName($currFirstName) as $currFN) { + if ( + NameMatcher::matchByInitials($currLN, $currFN, $author['last'], $author['first']) + || NameMatcher::matchByInitials($currFN, $currLN, $author['last'], $author['first']) // reverse for Asian names which sometimes put family name first + ) { + $foundCurrInAuthorList = TRUE; + // Application::log("Found current $currFirstName $currLastName {$author['last']}, {$author['first']} in author list in $recordId:$instance $pmid", $pid); + break; + } + } + if ($foundCurrInAuthorList) { + break; + } + } + if ($foundCurrInAuthorList) { break; } } diff --git a/classes/REDCapManagement.php b/classes/REDCapManagement.php index 5c2eb67e..e386b113 100644 --- a/classes/REDCapManagement.php +++ b/classes/REDCapManagement.php @@ -601,8 +601,11 @@ public static function getSurveyResponseId($pid, $recordId, $instance, $instrume # so being looser for future adaptation seems more in order public static function cleanUpOldLogs($pid) { $module = Application::getModule(); - $numToDelete = 1000; + $numToDelete = 10000; // Changing to 10,000 from 1,000; might want to consider larger, but MySQL deletes sometimes lag when larger than 10k $numBatches = 0; + $messageFrequency = 20; + $secondLimit = 60 * 15; // maximum run time + $startTs = time(); $minDate = DateManagement::PHPTsToREDCapTs(strtotime("-18 months")); $params = [$minDate, $pid]; @@ -610,11 +613,12 @@ public static function cleanUpOldLogs($pid) { $fromAndWhereClause = "FROM $logEventTable WHERE ts <= ? AND project_id = ?"; $deleteSql = "DELETE $fromAndWhereClause LIMIT $numToDelete"; $selectSql = "SELECT log_event_id $fromAndWhereClause LIMIT 1"; + $startTs = time(); do { $module->query($deleteSql, $params); $result = $module->query($selectSql, $params); $numBatches++; - } while ($result->fetch_assoc()); + } while ($result->fetch_assoc() && (time() < $startTs + $secondLimit)); $minDate = DateManagement::PHPTsToREDCapTs(strtotime("-1 week")); $params = [$minDate, $pid]; @@ -622,11 +626,17 @@ public static function cleanUpOldLogs($pid) { $fromAndWhereClause = "FROM $logEventTable WHERE ts <= ? AND project_id = ? AND data_values LIKE '%summary_last_calculated = %'"; $deleteSql = "DELETE $fromAndWhereClause LIMIT $numToDelete"; $selectSql = "SELECT log_event_id $fromAndWhereClause LIMIT 1"; + $batchStartTs = time(); do { $module->query($deleteSql, $params); $result = $module->query($selectSql, $params); $numBatches++; - } while ($result->fetch_assoc()); + if ($numBatches % $messageFrequency == 0) { + $batchEndTs = time(); + Application::log("On iteration $numBatches, deleted another ".($messageFrequency * $numToDelete)." rows in ".($batchEndTs - $batchStartTs)." seconds", $pid); + $batchStartTs = $batchEndTs; + } + } while ($result->fetch_assoc() && (time() < $startTs + $secondLimit)); Application::log("Estimated number of event_log rows cleaned: ".($numBatches * $numToDelete), $pid); } @@ -1547,11 +1557,16 @@ public static function setupSurveys($projectId, $surveysAndLabels) { DataDictionaryManagement::setupSurveys($projectId, $surveysAndLabels); } - public static function getPIDFromToken($token, $server) { - $projectSettings = Download::getProjectSettings($token, $server); - if (isset($projectSettings['project_id'])) { - return $projectSettings['project_id']; - } + public static function getPIDFromToken($token, $server) + { + $projectSettings = Download::getProjectSettings($token, $server); + if (isset($projectSettings['project_id'])) { + return $projectSettings['project_id']; + } + return self::getPIDFromTokenFromDatabase($token); + } + + public static function getPIDFromTokenFromDatabase($token) { $module = Application::getModule(); if ($module && self::isValidToken($token)) { $sql = "SELECT project_id FROM redcap_user_rights WHERE api_token = ?"; @@ -1560,7 +1575,15 @@ public static function getPIDFromToken($token, $server) { return $row['project_id']; } } - throw new \Exception("Could not get project-id from project settings: ".self::json_encode_with_spaces($projectSettings)); + throw new \Exception("Could not get project-id from token!"); + } + + public static function getPIDFromToken($token, $server) { + $projectSettings = Download::getProjectSettings($token, $server); + if (isset($projectSettings['project_id'])) { + return $projectSettings['project_id']; + } + return self::getPIDFromTokenFromDatabase($token); } public static function getOptionalFieldsNumber($field) { diff --git a/classes/ReactNIHTables.php b/classes/ReactNIHTables.php index 13cbfb6c..cf16bc1f 100644 --- a/classes/ReactNIHTables.php +++ b/classes/ReactNIHTables.php @@ -222,6 +222,7 @@ public function sendVerificationEmail($post, &$nihTables) { } $stringsToReplace = [ "[Relevant Table 2 & 4 Rows]", + "[Relevant Table 2 & 4 Rows]", "[Relevant Table 3 Rows]", ]; $replacedTables = Sanitizer::sanitizeWithoutStrippingHTML($post['mssg'], FALSE); diff --git a/classes/StarBRITE.php b/classes/StarBRITE.php index bc160845..c389201f 100644 --- a/classes/StarBRITE.php +++ b/classes/StarBRITE.php @@ -7,7 +7,7 @@ class StarBRITE { static function getServer() { $server = "starbrite.app.vumc.org"; - if (SERVER_NAME == "redcaptest.vanderbilt.edu") { + if (SERVER_NAME == "redcaptest.vumc.org") { $server = "starbritetest.app.vumc.org"; } return $server; diff --git a/classes/Workday.php b/classes/Workday.php new file mode 100644 index 00000000..6be34c73 --- /dev/null +++ b/classes/Workday.php @@ -0,0 +1,142 @@ +pid = $pid; + $this->file = self::getLatestFile(); + } + + public function searchForFirstName($firstName) { + return $this->searchForOneItem($firstName, "FIRST_NAME"); + } + + public function searchForLastName($lastName) { + return $this->searchForOneItem($lastName, "LAST_NAME"); + } + + private function searchForOneItem($name, $headerLabel) { + $name = strtolower(trim($name)); + if ($name === "") { + return []; + } + $matchedLines = []; + $headers = []; + if (file_exists($this->file)) { + $fp = fopen($this->file, "r"); + $headers = fgetcsv($fp); + $index = self::getIndexInArray($headerLabel, $headers); + if ($index >= 0) { + while ($line = fgetcsv($fp)) { + $lineName = strtolower($line[$index] ?? ""); + if ($lineName === $name) { + $matchedLines[] = $line; + } + } + } + } + Application::log("Search for $name in $headerLabel yielded ".count($matchedLines), $this->pid); + return self::shareData($matchedLines, $headers); + } + + public function searchForName($firstName, $lastName) { + $matchedLines = []; + $headers = []; + if (file_exists($this->file)) { + $fp = fopen($this->file, "r"); + $headers = fgetcsv($fp); + $lastNameIndex = self::getIndexInArray("LAST_NAME", $headers); + $firstNameIndex = self::getIndexInArray("FIRST_NAME", $headers); + if (($firstNameIndex >= 0) && ($lastNameIndex >= 0)) { + while ($line = fgetcsv($fp)) { + $lineFN = $line[$firstNameIndex] ?? ""; + $lineLN = $line[$lastNameIndex] ?? ""; + if (NameMatcher::matchName($lineFN, $lineLN, $firstName, $lastName)) { + $matchedLines[] = $line; + } + } + } + } + return self::shareData($matchedLines, $headers); + } + + public static function getViewableFields() { + return [ + "Employee ID" => "EMPLID", + "User ID" => "VUNETID", + "Email Address" => "EMAIL_ADDRESS", + "First Name" => "FIRST_NAME", + "Last Name" => "LAST_NAME", + "Address Line 1" => "ADDRESS_LINE_1", + "Address Line 2" => "ADDRESS_LINE_2", + "Vanderbilt Phone" => "VU_PHONE", + "Zip" => "ZIP", + "Business Phone" => "BUSINESS_PHONE", + "Hire Date/Time" => "HIRE_DT", + "Full-Time Employee" => "FTE", + "Region" => "REG_REGION", + "Department ID" => "DEPTID", + "Department Description" => "DESCR", + "Job Description" => "JOBCODE_DESCR", + "Job Function" => "JOB_FUNCTION", + "Job Family" => "JOB_FAMILY", + "Adjusted Manager Level" => "ADJUSTED_MANAGER_LEVEL", + "Department Manager Name" => "HD_DEPT_MANAGER_NAME", + "Department Manager ID" => "HD_DEPT_MANAGER_ID", + ]; + } + + private static function shareData($matchedLines, $headers) { + if (empty($headers) || empty($matchedLines)) { + return []; + } + + # for security, fields have been cleared with Kyle McGuffin in 1:1 conversation + $viewableColumns = self::getViewableFields(); + $assocArys = []; + foreach ($matchedLines as $line) { + $row = []; + foreach ($viewableColumns as $descr => $id) { + $index = self::getIndexInArray($id, $headers); + if (isset($line[$index])) { + $row[$descr] = $line[$index]; + } + } + if (!empty($row)) { + $assocArys[] = $row; + } + } + return $assocArys; + } + + public function searchForUserid($userid) { + return $this->searchForOneItem($userid, "VUNETID"); + } + + public static function getIndexInArray($col, $ary) { + foreach ($ary as $i => $val) { + if ($val == $col) { + return $i; + } + } + return -1; + } + + public static function getLatestFile() { + $credentialsFile = Application::getCredentialsDir()."/career_dev/workday.php"; + if (file_exists($credentialsFile)) { + include($credentialsFile); + if (isset($location) && isset($prefix)) { + $ts = strtotime("-1 day"); + return "$location/$prefix".date("Ymd", $ts).".csv"; + } + } + return ""; + } + + protected $pid; + protected $file; +} \ No newline at end of file diff --git a/config.php b/config.php index 8684662e..b6e87cf2 100644 --- a/config.php +++ b/config.php @@ -440,7 +440,7 @@ function makeSettings($module, $pid) { $ary["Proxy Server (Only if Applicable)"][] = makeSetting("proxy-pass", "text", "Proxy Password"); $ary["REDCap Configuration"] = []; - $ary["REDCap Configuration"][] = makeSetting("safe_servers", "text", "Comma-separated list of domain names (e.g., redcap.vanderbilt.edu) of other external servers that (e.g., a separate REDCap Survey Server) that can access Flight Tracker"); + $ary["REDCap Configuration"][] = makeSetting("safe_servers", "text", "Comma-separated list of domain names (e.g., redcap.vumc.org) of other external servers that (e.g., a separate REDCap Survey Server) that can access Flight Tracker"); $html = ""; if ($module) { diff --git a/copyProject.php b/copyProject.php index ecf7219e..d9207b12 100644 --- a/copyProject.php +++ b/copyProject.php @@ -115,7 +115,7 @@

An API Token for the new project is required, even if it's on the same server. The new project needs the Flight Tracker for Scholars External Module enabled, but it does not need to be set up unless you check the checkbox just to add your data. Finally, the REDCap user with the API Token needs to have both API Import and API Export rights.


-


+


diff --git a/cronLoad.php b/cronLoad.php index f946fdc9..48736d13 100644 --- a/cronLoad.php +++ b/cronLoad.php @@ -67,6 +67,21 @@ function runMainCrons(&$manager, $token, $server) { $manager->addCron("drivers/23_getERIC.php", "getERIC", "Friday", $records, 100); } + if (Application::isServer("redcap.vanderbilt.edu") || Application::isServer("redcap.vumc.org")) { + if (in_array('ldapds', $forms)) { + $manager->addCron("drivers/17_getLDAP.php", "getLDAPs", "Monday", $allRecords, 10000); + } + $manager->addCron("drivers/grantRepositoryFetch.php", "checkGrantRepository", "Monday", $allRecords, 500); + $manager->addCron("drivers/2p_updateStudioUse.php", "copyStudios", "Friday", $allRecords, 500); + $manager->addCron("drivers/importHistoricalCOEUS.php", "importHistoricalCOEUS", "2024-03-25", $allRecords, 10000); + if (in_array('coeus', $forms)) { + # Put in Multi crons + // $manager->addCron("drivers/19_updateNewCoeus.php", "updateAllCOEUS", "Wednesday", $allRecords, 1000); + $manager->addCron("drivers/19_updateNewCoeus.php", "sendUseridsToCOEUS", "Friday", $allRecords, 500); + } else if (in_array('coeus2', $forms)) { + $manager->addCron("drivers/2r_updateCoeus2.php", "processCoeus2", "Thursday", $records, 100); + } + } } catch(\Exception $e) { Application::log("ERROR in runMainCrons: ".$e->getMessage(), $pid); } @@ -78,7 +93,6 @@ function runIntenseCrons(&$manager, $token, $server) { Application::log("loadIntenseCrons", $pid); try { - $forms = Download::metadataForms($token, $server); $metadataFields = Download::metadataFields($token, $server); $switches = new FeatureSwitches($token, $server, $pid); if (in_array("identifier_stop_collection", $metadataFields)) { @@ -87,9 +101,6 @@ function runIntenseCrons(&$manager, $token, $server) { $allRecords = Download::recordIds($token, $server); } $records = $switches->downloadRecordIdsToBeProcessed($allRecords); - $securityTestMode = Application::getSetting("security_test_mode", $pid); - - $manager->addCron("drivers/updateInstitution.php", "updateInstitution", "Saturday", $allRecords, 10000); if (!Application::getSetting("dedupResources122023", $pid)) { $manager->addCron("drivers/preprocess.php", "dedupResources", date("Y-m-d")); @@ -124,22 +135,6 @@ function runIntenseCrons(&$manager, $token, $server) { } } - if (in_array('ldapds', $forms)) { - $manager->addCron("drivers/17_getLDAP.php", "getLDAPs", "Monday", $allRecords, 10000); - } - if (!Application::isLocalhost() && Application::isVanderbilt() && !Application::isServer("redcaptest.vanderbilt.edu")) { - # only on redcap.vanderbilt.edu - $manager->addCron("drivers/grantRepositoryFetch.php", "checkGrantRepository", "Monday", $allRecords, 500); - $manager->addCron("drivers/2p_updateStudioUse.php", "copyStudios", "Friday", $allRecords, 500); - $manager->addCron("drivers/importHistoricalCOEUS.php", "importHistoricalCOEUS", "2024-03-25", $allRecords, 10000); - if (in_array('coeus', $forms)) { - # Put in Multi crons - // $manager->addCron("drivers/19_updateNewCoeus.php", "updateAllCOEUS", "Wednesday", $allRecords, 1000); - $manager->addCron("drivers/19_updateNewCoeus.php", "sendUseridsToCOEUS", "Friday", $allRecords, 500); - } else if (in_array('coeus2', $forms)) { - $manager->addCron("drivers/2r_updateCoeus2.php", "processCoeus2", "Thursday", $records, 100); - } - } $manager->addCron("drivers/12_reportStats.php", "reportStats", "Friday", $allRecords, 100000); $celebrations = new CelebrationsEmail($token, $server, $pid, []); @@ -229,7 +224,6 @@ function loadInitialCrons(&$manager, $specialOnly = FALSE, $token = "", $server if (Application::isVanderbilt() && !Application::isLocalhost() && in_array("pre_screening_survey", $forms)) { $manager->addCron("drivers/11_vfrs.php", "updateVFRS", $date, $records, 100); } - $manager->addCron("drivers/updateInstitution.php", "updateInstitution", "Tuesday"); if (in_array("coeus", $forms)) { $manager->addCron("drivers/19_updateNewCoeus.php", "updateCOEUSGrants", $date, $records, 500); } else if (in_array("coeus2", $forms)) { @@ -309,7 +303,7 @@ function loadMultiProjectCrons(&$manager, $pids) $manager->addMultiCron("drivers/19_updateNewCoeus.php", "updateAllCOEUSMulti", "Wednesday", $pids); $manager->addMultiCron("drivers/22_getVERA.php", "getVERAMulti", "Monday", $pids); $manager->addMultiCron("drivers/updateAllMetadata.php", "updateMetadataMulti", "Monday", $pids); - if (Application::isServer("redcap.vanderbilt.edu")) { + if (Application::isServer("redcap.vanderbilt.edu") || Application::isServer("redcap.vumc.org")) { $manager->addMultiCron("drivers/26_workday.php", "getAllWorkday", "Friday", $pids); } } @@ -325,7 +319,7 @@ function loadMultiProjectCrons(&$manager, $pids) function loadInternalSharingCrons(&$manager, $pids) { $preprocessingPids = []; - if (Application::isVanderbilt() && Application::isServer("redcap.vanderbilt.edu")) { + if (Application::isVanderbilt() && (Application::isServer("redcap.vanderbilt.edu") || Application::isServer("redcap.vumc.org"))) { $preprocessingPids[] = NEWMAN_SOCIETY_PROJECT; } else if (Application::isVanderbilt() && Application::isLocalhost()) { $preprocessingPids[] = LOCALHOST_TEST_PROJECT; @@ -351,7 +345,7 @@ function loadInternalSharingCrons(&$manager, $pids) { if (Application::isLocalhost()) { $manager->addMultiCron("drivers/preprocess.php", "preprocessFindSharingMatches", date("Y-m-d"), $preprocessingPids); $manager->addMultiCron("drivers/preprocess.php", "preprocessMissingEmails", date("Y-m-d"), $preprocessingPids); - } else if (Application::isServer("redcaptest.vanderbilt.edu")) { + } else if (Application::isServer(FlightTrackerExternalModule::VANDERBILT_TEST_SERVER)) { $manager->addMultiCron("drivers/preprocess.php", "preprocessFindSharingMatches", date("Y-m-d"), $preprocessingPids); $manager->addMultiCron("drivers/preprocess.php", "preprocessSharingMatches", date("Y-m-d"), $preprocessingPids); } else { diff --git a/dashboard/activities.php b/dashboard/activities.php index 6c5558a5..52fd590c 100755 --- a/dashboard/activities.php +++ b/dashboard/activities.php @@ -51,8 +51,10 @@ $instruments = ["honors_awards_and_activities", "honors_awards_and_activities_survey"]; $counts = [TOTAL_LABEL => []]; +$indexedRedcapData = []; foreach ($records as $recordId) { $redcapData = Download::fieldsForRecordsByPid($pid, $fields, [$recordId]); + $indexedRedcapData[$recordId] = $redcapData; foreach ($redcapData as $row) { if (in_array($row['redcap_repeat_instrument'], $instruments)) { $prefix = REDCapManagement::getPrefixFromInstrument($row['redcap_repeat_instrument']); diff --git a/drivers/11_vfrs.php b/drivers/11_vfrs.php index 38b1e95c..ced0b96f 100644 --- a/drivers/11_vfrs.php +++ b/drivers/11_vfrs.php @@ -28,7 +28,7 @@ function updateVFRS($token, $server, $pid, $records) { function updateVFRSMulti($pids) { Application::log("updateVFRSMulti with ".count($pids)." pids"); $vfrs_token = CareerDev::getVFRSToken(); - $vfrsData = Download::fields($vfrs_token, 'https://redcap.vanderbilt.edu/api/', ["participant_id", "name_first", "name_last"]); + $vfrsData = Download::fields($vfrs_token, 'https://redcap.vumc.org/api/', ["participant_id", "name_first", "name_last"]); foreach ($pids as $currPid) { $currToken = Application::getSetting("token", $currPid); diff --git a/drivers/12_reportStats.php b/drivers/12_reportStats.php index 4a5a30c5..b4b403fd 100644 --- a/drivers/12_reportStats.php +++ b/drivers/12_reportStats.php @@ -9,7 +9,10 @@ # Reports figures like number of scholars for each project. function reportStats($token, $server, $pid, $records) { - $url = "https://redcap.vanderbilt.edu/plugins/career_dev/receiveStats.php"; + $urls = [ + "https://redcap.vumc.org/plugins/career_dev/receiveStats.php", + "https://redcap.vanderbilt.edu/plugins/career_dev/receiveStats.php" + ]; # do NOT report details of records; just report: number of records/scholars $recordIds = Download::recordIds($token, $server); @@ -29,20 +32,36 @@ function reportStats($token, $server, $pid, $records) { // "grants" => $numGrants, // "publications" => $numPubs, - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_VERBOSE, 0); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_AUTOREFERER, true); - curl_setopt($ch, CURLOPT_MAXREDIRS, 10); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); - curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post, '', '&')); - $output = curl_exec($ch); - CareerDev::log($output); - curl_close($ch); + $url = ""; + foreach ($urls as $u) { + try { + if (URLManagement::isGoodURL($u)) { + $url = $u; + break; + } + } catch (\Exception $e) { + # do nothing + } + } + if ($url) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_VERBOSE, 0); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_AUTOREFERER, true); + curl_setopt($ch, CURLOPT_MAXREDIRS, 10); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post, '', '&')); + URLManagement::applyProxyIfExists($ch, $pid); + $output = curl_exec($ch); + Application::log($output, $pid); + curl_close($ch); + } else { + Application::log("Cannot report stats!", $pid); + } } } diff --git a/drivers/2a_initializeData.php b/drivers/2a_initializeData.php index 7d522b02..ba072a72 100755 --- a/drivers/2a_initializeData.php +++ b/drivers/2a_initializeData.php @@ -20,7 +20,7 @@ } # Token for VFRS -$vfrs_token = 'A987974FEEBDA008EB3200B182EAD1EE'; +$vfrs_token = ''; # all of the newman data # a prefix differentiates the data in the REDCap database @@ -173,7 +173,7 @@ function handleFailedData($row) { 'returnFormat' => 'json' ); $ch = curl_init(); -curl_setopt($ch, CURLOPT_URL, 'https://redcap.vanderbilt.edu/api/'); +curl_setopt($ch, CURLOPT_URL, 'https://redcap.vumc.org/api/'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_VERBOSE, 0); diff --git a/drivers/2e_ho_vs_holden.php b/drivers/2e_ho_vs_holden.php index d245f75e..317855f6 100755 --- a/drivers/2e_ho_vs_holden.php +++ b/drivers/2e_ho_vs_holden.php @@ -251,7 +251,7 @@ function handleFailedData($row) { 'returnFormat' => 'json' ); $ch = curl_init(); -curl_setopt($ch, CURLOPT_URL, 'https://redcap.vanderbilt.edu/api/'); +curl_setopt($ch, CURLOPT_URL, 'https://redcap.vumc.org/api/'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_VERBOSE, 0); diff --git a/drivers/2g_restoreNonVUMCReporters.php b/drivers/2g_restoreNonVUMCReporters.php index b95c9acd..ba8c7635 100644 --- a/drivers/2g_restoreNonVUMCReporters.php +++ b/drivers/2g_restoreNonVUMCReporters.php @@ -3,7 +3,7 @@ require_once("small_base.php"); $backup_token = "DAEF83030E6CD5703582DCE8EB4E8406"; -$backup_server = "https://redcap.vanderbilt.edu/api/"; +$backup_server = "https://redcap.vumc.org/api/"; echo "Download reporter fields\n"; $data = array( diff --git a/drivers/6d_makeSummary.php b/drivers/6d_makeSummary.php index b95df8d3..22df5642 100755 --- a/drivers/6d_makeSummary.php +++ b/drivers/6d_makeSummary.php @@ -6,8 +6,9 @@ # used every time that the summaries are recalculated # 30 minute runtimes -require_once(dirname(__FILE__)."/../classes/Autoload.php"); -require_once(dirname(__FILE__)."/../small_base.php"); +require_once(__DIR__."/../classes/Autoload.php"); +require_once(__DIR__."/../small_base.php"); +require_once(__DIR__."/updateInstitution.php"); function getLockFile($pid) { return CareerDev::getLockFile($pid); @@ -34,6 +35,7 @@ function unlock($pid) { } function makeSummary($token, $server, $pid, $records, $runAllRecords = FALSE) { + updateInstitution($token, $server, $pid, $records); if (!is_array($records)) { $records = [$records]; $runAllRecords = TRUE; @@ -60,6 +62,8 @@ function makeSummary($token, $server, $pid, $records, $runAllRecords = FALSE) { gc_collect_cycles(); } + REDCapManagement::cleanUpOldLogs($pid); + unlock($pid); } diff --git a/drivers/8_copyOverrides.php b/drivers/8_copyOverrides.php index 6ffa1f83..40d00219 100644 --- a/drivers/8_copyOverrides.php +++ b/drivers/8_copyOverrides.php @@ -14,7 +14,7 @@ 'returnFormat' => 'json', ); $ch = curl_init(); -curl_setopt($ch, CURLOPT_URL, 'https://redcap.vanderbilt.edu/api/'); +curl_setopt($ch, CURLOPT_URL, 'https://redcap.vumc.org/api/'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_VERBOSE, 0); @@ -43,7 +43,7 @@ 'returnFormat' => 'json', ); $ch = curl_init(); -curl_setopt($ch, CURLOPT_URL, 'https://redcap.vanderbilt.edu/api/'); +curl_setopt($ch, CURLOPT_URL, 'https://redcap.vumc.org/api/'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_VERBOSE, 0); @@ -88,7 +88,7 @@ 'returnFormat' => 'json' ); $ch = curl_init(); -curl_setopt($ch, CURLOPT_URL, 'https://redcap.vanderbilt.edu/api/'); +curl_setopt($ch, CURLOPT_URL, 'https://redcap.vumc.org/api/'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_VERBOSE, 0); diff --git a/drivers/clearReporters.php b/drivers/clearReporters.php index c143b7c6..7d3ab22d 100644 --- a/drivers/clearReporters.php +++ b/drivers/clearReporters.php @@ -35,7 +35,7 @@ if ($row['redcap_repeat_instrument'] == "reporter") { echo "Deleting record {$row['record_id']} instance {$row['redcap_repeat_instance']}\n"; $ch = curl_init(); - $url = "https://redcap.vanderbilt.edu/plugins/career_dev/drivers/deleteInstance.php?pid=$pid&instrument=reporter&instance=".$row['redcap_repeat_instance']."&record=".$row['record_id']; + $url = "https://redcap.vumc.org/plugins/career_dev/drivers/deleteInstance.php?pid=$pid&instrument=reporter&instance=".$row['redcap_repeat_instance']."&record=".$row['record_id']; curl_setopt($ch, CURLOPT_URL, $url); $output = ""; $output = curl_exec($ch); diff --git a/help/excelToFT.php b/help/excelToFT.php index 0a0d13e9..4ca3b272 100644 --- a/help/excelToFT.php +++ b/help/excelToFT.php @@ -5,5 +5,5 @@ require_once(dirname(__FILE__)."/../charts/baseWeb.php"); require_once(dirname(__FILE__)."/../CareerDev.php"); -echo "\n"; +echo "\n"; diff --git a/help/install.php b/help/install.php index 2434b16a..b7d4dc0b 100644 --- a/help/install.php +++ b/help/install.php @@ -1,4 +1,4 @@ Flight Tracker Installation - + \ No newline at end of file diff --git a/help/intro.php b/help/intro.php index 8d7331e2..419c60b0 100644 --- a/help/intro.php +++ b/help/intro.php @@ -5,5 +5,5 @@ require_once(dirname(__FILE__)."/../charts/baseWeb.php"); require_once(dirname(__FILE__)."/../CareerDev.php"); -echo "\n"; +echo "\n"; diff --git a/help/videos.php b/help/videos.php index 85405641..afe5e94a 100644 --- a/help/videos.php +++ b/help/videos.php @@ -6,7 +6,7 @@ require_once(dirname(__FILE__)."/../CareerDev.php"); require_once(dirname(__FILE__)."/../charts/baseWeb.php"); -$home = "https://redcap.vanderbilt.edu/plugins/career_dev/help/"; +$home = "https://redcap.vumc.org/plugins/career_dev/help/"; echo "

Training Videos

\n"; diff --git a/hooks/dataFormHook.php b/hooks/dataFormHook.php index 7d6ddedf..78350032 100644 --- a/hooks/dataFormHook.php +++ b/hooks/dataFormHook.php @@ -17,5 +17,6 @@ echo "$('[data-mlm-name=$instrument]').on('click', () => { $('[data-mlm-name=$instrument]').next().slideDown(); });\n"; echo "$('[data-mlm-name=$instrument]').on('mouseout', () => { $('[data-mlm-name=$instrument]').next().slideUp(); });\n"; } +echo "$('#center .yellow').hide();\n"; // note that project is in development status echo "});\n"; echo ""; \ No newline at end of file diff --git a/index.php b/index.php index 3e5d3775..abeff78a 100644 --- a/index.php +++ b/index.php @@ -130,7 +130,7 @@

Flight Tracker Central

v

-

Watch Your Scholars Fly - Flight Tracker Community Support

+

Watch Your Scholars Fly - Flight Tracker Community Support

from " alt="Edge for Scholars" style="width: 27px; height: 20px;"> Edge for Scholars
Consortium

About the Consortium

Monthly Planning Meetings

-

Next meeting is on , at 1pm CT (2pm ET, 11am PT). Email Scott Pearson for an invitation. (View agenda.)

+

Next meeting is on , at 1pm CT (2pm ET, 11am PT). Email Scott Pearson for an invitation. (View agenda.)

diff --git a/install.php b/install.php index accf2c25..3d39c237 100644 --- a/install.php +++ b/install.php @@ -173,8 +173,8 @@ function makeDepartmentPrompt($projectId) { $html .= ""; if (Application::isVanderbilt()) { - list($respDepts, $defaultDepartments) = REDCapManagement::downloadURL("https://redcap.vanderbilt.edu/plugins/career_dev/data/departments.txt"); - list($respResources, $defaultResources) = REDCapManagement::downloadURL("https://redcap.vanderbilt.edu/plugins/career_dev/data/resources.txt"); + list($respDepts, $defaultDepartments) = REDCapManagement::downloadURL("https://redcap.vumc.org/plugins/career_dev/data/departments.txt"); + list($respResources, $defaultResources) = REDCapManagement::downloadURL("https://redcap.vumc.org/plugins/career_dev/data/resources.txt"); if (($respDepts != 200) || ($respResources != 200)) { $defaultDepartments = ""; $defaultResources = ""; diff --git a/metadata.json b/metadata.json index 38ea5e96..ba643139 100644 --- a/metadata.json +++ b/metadata.json @@ -427,7 +427,7 @@ {"field_name":"check_degree0_year","form_name":"initial_survey","section_header":"","field_type":"text","field_label":"","select_choices_or_calculations":"","field_note":"Year (YYYY)","text_validation_type_or_show_slider_number":"integer","text_validation_min":"1920","text_validation_max":"2050","identifier":"","branching_logic":"[check_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"check_degree0_institution","form_name":"initial_survey","section_header":"","field_type":"text","field_label":"Institution that will grant this degree","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"[check_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"check_degree0_prior_rsch","form_name":"initial_survey","section_header":"","field_type":"text","field_label":"Months of prior, full-time research experience","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"integer","text_validation_min":"0","text_validation_max":"","identifier":"","branching_logic":"[check_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, - {"field_name":"check_degree0_topic","form_name":"initial_survey","section_header":"","field_type":"text","field_label":"Topic of Research Project","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"[check_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":"@CHARLIMIT=100"}, + {"field_name":"check_degree0_topic","form_name":"initial_survey","section_header":"","field_type":"text","field_label":"Topic of Research Project","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"[check_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"check_d0_1","form_name":"initial_survey","section_header":"","field_type":"descriptive","field_label":"
Undergraduate degree program<\/h5>","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"check_undergrad_institution","form_name":"initial_survey","section_header":"","field_type":"text","field_label":"From which institution did you receive or do you expect to receive your undergraduate degree?","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"check_undergrad_month","form_name":"initial_survey","section_header":"","field_type":"dropdown","field_label":"When did you receive or do you expect to receive your undergraduate degree? (Please select month and enter year)","select_choices_or_calculations":"01, January | 02, February | 03, March | 04, April | 05, May | 06, June | 07, July | 08, August | 09, September | 10, October | 11, November | 12, December","field_note":"Month","text_validation_type_or_show_slider_number":"autocomplete","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, @@ -1528,7 +1528,7 @@ {"field_name":"init_import_degree0_year","form_name":"initial_import","section_header":"","field_type":"text","field_label":"","select_choices_or_calculations":"","field_note":"Year (YYYY)","text_validation_type_or_show_slider_number":"integer","text_validation_min":"1920","text_validation_max":"2050","identifier":"","branching_logic":"[init_import_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"init_import_degree0_institution","form_name":"initial_import","section_header":"","field_type":"text","field_label":"Institution that will grant this degree","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"[init_import_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"init_import_degree0_prior_rsch","form_name":"initial_import","section_header":"","field_type":"text","field_label":"Months of prior, full-time research experience","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"integer","text_validation_min":"0","text_validation_max":"","identifier":"","branching_logic":"[init_import_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, - {"field_name":"init_import_degree0_topic","form_name":"initial_import","section_header":"","field_type":"text","field_label":"Topic of Research Project","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"[init_import_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":"@CHARLIMIT=100"}, + {"field_name":"init_import_degree0_topic","form_name":"initial_import","section_header":"","field_type":"text","field_label":"Topic of Research Project","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"[init_import_degree0_yesno] = '1'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"init_import_d1","form_name":"initial_import","section_header":"

Education

","field_type":"descriptive","field_label":"
Most recent graduate degree
","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"init_import_degree1","form_name":"initial_import","section_header":"","field_type":"dropdown","field_label":"Please select the most recent graduate degree you received","select_choices_or_calculations":"md,MD | phd,PhD | mdphd,MD/PhD | do,DO | aud,AuD | ba,BA | bba,BBA | be,BE | beng,BEng | bs,BS | bsed,BSEd | bsn,BSN | bsw,BSW | rn,RN | ma,MA | maed,MAEd | mba,MBA | mdiv,MDiv | med,MEd | meng,MEng | mgc,MGC | mhs,MHS | mpa,MPA | mph,MPH | ms,MS | msci,MSCI | mscr,MSCR | msmp,MSMP | msn,MSN | msw,MSW | mssw,MSSW | mts,MTS | mbbs,MBBS | mbchb,MBChB | dmp,DMP | dphil,DPhil | dnp,DNP | drph,DrPH | dsw,DSW | edd,EdD | jd,JD | pharmd,PharmD | psyd,PsyD | scid,SciD | 99,Other","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, {"field_name":"init_import_degree1_oth","form_name":"initial_import","section_header":"","field_type":"text","field_label":"Please specify (e.g., DPhil):","select_choices_or_calculations":"","field_note":"","text_validation_type_or_show_slider_number":"","text_validation_min":"","text_validation_max":"","identifier":"","branching_logic":"[init_import_degree1] = '99'","required_field":"","custom_alignment":"","question_number":"","matrix_group_name":"","matrix_ranking":"","field_annotation":""}, diff --git a/portal/driver.php b/portal/driver.php index 5bb74f6f..cf441a40 100644 --- a/portal/driver.php +++ b/portal/driver.php @@ -129,10 +129,10 @@ $data['error'] = "No file found."; } } else if ($action == "stats") { - if (SERVER_NAME == "redcap.vanderbilt.edu") { - $url = "https://redcap.vanderbilt.edu/plugins/career_dev/login/figuresBehindREDCap.php"; + if (Application::isServer("redcap.vanderbilt.edu") || Application::isServer("redcap.vumc.org")) { + $url = "https://redcap.vumc.org/plugins/career_dev/login/figuresBehindREDCap.php"; } else if (Application::isVanderbilt()) { - $url = "https://redcap.vanderbilt.edu/plugins/career_dev/newmanFigures"; + $url = "https://redcap.vumc.org/plugins/career_dev/newmanFigures"; } else { $url = ""; } diff --git a/publications/getAllPubs_func.php b/publications/getAllPubs_func.php index 0629052b..2b37833f 100644 --- a/publications/getAllPubs_func.php +++ b/publications/getAllPubs_func.php @@ -51,7 +51,11 @@ function getPubsGeneric($token, $server, $pid, $records, $searchWithInstitutions Application::log("Uploaded $cnt blank rows for $recordId", $pid); } } - Publications::deleteMismatchedRows($token, $server, $pid, $recordId, $firstNames, $lastNames); + # 2024-04-17 - I'm removing this line because it sometimes acts incorrectly due to data irregularities in PubMed + # It also frustrates users who try to add back items that have such irregularities, only to have them deleted automatically + # The matching algorithm is better now than in the past, so I don't think this is needed. + # I'm leaving the code in here for now in case we want to turn it on. + // Publications::deleteMismatchedRows($token, $server, $pid, $recordId, $firstNames, $lastNames); if (hasBlankCitationField($redcapData, "citation_pmcid")) { Publications::updateNewPMCs($token, $server, $pid, $recordId, $redcapData); } diff --git a/reporting/getData.php b/reporting/getData.php index 747c07bf..a47c0637 100644 --- a/reporting/getData.php +++ b/reporting/getData.php @@ -12,7 +12,7 @@ require_once(dirname(__FILE__)."/../classes/Autoload.php"); define('NOAUTH', TRUE); -Application::applySecurityHeaders(); +Application::applySecurityHeaders($_GET['pid'] ?? $_GET['project_id'] ?? NULL); require_once(dirname(__FILE__)."/../small_base.php"); diff --git a/reporting/index.php b/reporting/index.php index a5cef467..4cece887 100644 --- a/reporting/index.php +++ b/reporting/index.php @@ -7,12 +7,31 @@ use \Vanderbilt\CareerDevLibrary\Download; use \Vanderbilt\CareerDevLibrary\Cohorts; use \Vanderbilt\CareerDevLibrary\REDCapManagement; +use Vanderbilt\CareerDevLibrary\URLManagement; require_once(dirname(__FILE__)."/../charts/baseWeb.php"); require_once(dirname(__FILE__)."/../classes/Autoload.php"); Application::increaseProcessingMax(1); +function makeMonthLink($tableNum) { + $thisLink = Application::link("this"); + if (isset($_GET['cohort']) && $_GET['cohort']) { + $thisLink .= "&cohort=".urlencode(REDCapManagement::sanitizeCohort($_GET['cohort'])); + } + $basePage = URLManagement::getPage($thisLink); + $hiddenHTML = URLManagement::getParametersAsHiddenInputs($thisLink); + $title = NIHTables::getTableHeader($tableNum); + return "
+ $hiddenHTML +

+ Table $tableNum - $title
+ Show Publications in Training Plus for Publication Delays
+ +

+
"; +} + function makeLink($tableNum) { if ($tableNum == "Common Metrics") { $link = Application::link("reporting/commonMetrics.php"); @@ -71,7 +90,7 @@ function makeTableHeader($tableNum) {

-

".makeLink("5B-VUMC")."

\n"; + echo makeMonthLink("5B-VUMC"); } else { - echo "

".makeLink("5B")."

\n"; + echo makeMonthLink("5B"); } if (Application::isVanderbilt() && Application::isPluginProject($pid) && !Application::isLocalhost()) { @@ -131,8 +151,8 @@ function makeTableHeader($tableNum) { } } -$part1Message = "Only scholars whose appointments overlap with the last ".NIHTables::PART_1_YEARS." are included in this table."; -$part3Message = "Only scholars whose appointments overlap with the last ".NIHTables::PART_3_YEARS." are included in this table."; +$part1Message = "Only scholars whose appointments overlap with the last ".NIHTables::PART_1_YEARS." years are included in this table."; +$part3Message = "Only scholars whose appointments overlap with the last ".NIHTables::PART_3_YEARS." years are included in this table."; ?>

@@ -168,7 +188,7 @@ function makeTableHeader($tableNum) { ?>

Use with xTRACT for Reporting to NIH

-

" alt="eRA Commons"> eRA Commons

+

" alt="eRA Commons"> eRA Commons

The eRA Commons recently updated its interface. This change made our bookmarklet obsolete. We plan to investigate revisions in the near future. Please contact scott.j.pearson@vumc.org if you'd like an update.