From: Chris Sharp Date: Sun, 15 Jan 2017 18:30:39 +0000 (-0500) Subject: Adding Chris DeBellis's Executive Reports work X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=134826d659b1839a738fd773c6df1d775cb9e10a;p=contrib%2Fpines%2Freport-creator.git Adding Chris DeBellis's Executive Reports work Signed-off-by: Chris Sharp --- diff --git a/config/app.config.php b/config/app.config.php index da8f77f..44eca97 100644 --- a/config/app.config.php +++ b/config/app.config.php @@ -28,6 +28,7 @@ define ('QR_LOGOUT_PAGE', '?logout/'); define ('QR_SHOW_EXECUTIVE_REPORTS_MENU_PAGE', '?executive/'); define ('QR_LIST_EXECUTIVE_REPORTS_PAGE', '?executive/reports/'); define ('QR_CREATE_EXECUTIVE_REPORT_PAGE', 'executiveReport.php?'); +define ('QR_EXECUTIVE_REPORTS_MENU_COLUMNS', 3); //login define ('PINS_LOGOUT_EXPLICIT', 1); @@ -48,4 +49,4 @@ define ('QR_MENU_ADMIN', '60'); //escape text shown in javascript alerts define ('QR_DEFAULT_ESCAPE_PATTERNS', serialize(array("/\\\\/", '/ /', '/ /', '/\n/', '/\r/', '/\t/', '/\v/', '/\f/', "/'/", '/"/', '/"/'))); define ('QR_DEFAULT_ESCAPE_REPLACEMENTS', serialize(array('\\\\\\', '\n', '\r', '\n', '\r', '\t', '\v', '\f', ''', '\"', '\"'))); -?> \ No newline at end of file +?> diff --git a/config/executiveReports.config.php b/config/executiveReports.config.php new file mode 100644 index 0000000..a60381b --- /dev/null +++ b/config/executiveReports.config.php @@ -0,0 +1,144 @@ + 'P1', + 'category' => 'Patrons', + 'name' => 'Active Users', + 'description' => 'Count of active users'); + +$report[] = (object) array ( + 'id' => 'P2', + 'category' => 'Patrons', + 'name' => 'New Users', + 'description' => 'Count of new users'); + +$report[] = (object) array ( + 'id' => 'P3', + 'category' => 'Patrons', + 'name' => 'Users Who Circulated Items', + 'description' => 'Count of users who circulated items'); + +$report[] = (object) array ( + 'id' => 'P4', + 'category' => 'Patrons', + 'name' => 'Users Who Placed Holds', + 'description' => 'Count of users who placed holds'); + +$report[] = (object) array ( + 'id' => 'B1', + 'category' => 'Bills', + 'format' => 'currency', + 'name' => 'Amount Owed By My Patrons', + 'description' => 'Total amount owed by my patrons'); + +$report[] = (object) array ( + 'id' => 'B2', + 'category' => 'Bills', + 'format' => 'currency', + 'name' => 'Amount Billed To My Patrons', + 'description' => 'Amount billed to my patrons this month'); + +$report[] = (object) array ( + 'id' => 'B3', + 'category' => 'Bills', + 'format' => 'currency', + 'name' => 'Amount Collected From My Patrons', + 'description' => 'Amount collected this month'); + +$report[] = (object) array ( + 'id' => 'C1', + 'category' => 'Circulation', + 'name' => 'Circulations', + 'description' => 'Count of circulation'); + +$report[] = (object) array ( + 'id' => 'C2', + 'category' => 'Circulation', + 'name' => 'Circulations by Circulation Modifier', + 'description' => 'Count of circulation by circulation modifier'); + +$report[] = (object) array ( + 'id' => 'C3', + 'category' => 'Circulation', + 'name' => 'Circulations by MARC Type', + 'description' => 'Count of circulation by MARC type'); + +$report[] = (object) array ( + 'id' => 'C4', + 'category' => 'Circulation', + 'name' => 'Circulations by Non-Cataloged', + 'description' => 'Count of circulation by non-cataloged types'); + +$report[] = (object) array ( + 'id' => 'C5', + 'category' => 'Circulation', + 'name' => 'In-House Use', + 'description' => 'Count of in-house use'); + +$report[] = (object) array ( + 'id' => 'H1', + 'category' => 'Holds/Transits', + 'name' => 'Holds Sent From My Library', + 'description' => 'Holds sent from my library'); + +$report[] = (object) array ( + 'id' => 'H2', + 'category' => 'Holds/Transits', + 'name' => 'Holds Received At My Library', + 'description' => 'Holds received at my library from another library'); + +$report[] = (object) array ( + 'id' => 'H3', + 'category' => 'Holds/Transits', + 'name' => 'Internal Holds', + 'description' => 'Internal holds'); + +$report[] = (object) array ( + 'id' => 'H4', + 'category' => 'Holds/Transits', + 'name' => 'Total Incoming Transits', + 'description' => 'Total incoming transits'); + +$report[] = (object) array ( + 'id' => 'H5', + 'category' => 'Holds/Transits', + 'name' => 'Total Outgoing Transits', + 'description' => 'Total outgoing transits'); + +$report[] = (object) array ( + 'id' => 'H6', + 'category' => 'Holds/Transits', + 'name' => 'Total IntraPINES Sent', + 'description' => 'Total IntraPINES sent (HQ-HQ)'); + +$report[] = (object) array ( + 'id' => 'H7', + 'category' => 'Holds/Transits', + 'name' => 'Total IntraPINES Received', + 'description' => 'Total IntraPINES received (HQ-HQ)'); + +$report[] = (object) array ( + 'id' => 'I1', + 'category' => 'Collections/Items', + 'name' => 'Total Items', + 'description' => 'Count of items'); + +$report[] = (object) array ( + 'id' => 'I2', + 'category' => 'Collections/Items', + 'format' => 'currency', + 'name' => 'Value of Items', + 'description' => 'Value of items'); + +$report[] = (object) array ( + 'id' => 'I3', + 'category' => 'Collections/Items', + 'name' => 'Added Items', + 'description' => 'Count of added items'); + +$report[] = (object) array ( + 'id' => 'I4', + 'category' => 'Collections/Items', + 'name' => 'Deleted Items', + 'description' => 'Count of deleted items'); + +?> diff --git a/config/production.config.php b/config/production.config.php index 9744bf8..e0ceac0 100644 --- a/config/production.config.php +++ b/config/production.config.php @@ -2,18 +2,31 @@ //system configs define ('QR_ERROR_REPORTING_LEVEL', E_ALL); define ('QR_DEFAULT_TIME_ZONE', 'America/New_York'); -define ('QR_SITE_ROOT', '/report-creator-generic/'); define ('QR_REPORT_OUTPUT_URL_PREFIX', 'https://next.gapines.org/reporter/'); +define ('QR_SITE_ROOT', '/report-creator/'); //define ('QR_REPORT_OUTPUT_URL_PREFIX', 'https://reports-dev.gapines.org/reporter/'); define ('QR_REPORT_OUTPUT_URL_SUFFIX', '/report-data.html'); //page headings define ('QR_DEFAULT_LOGO_TEXT', 'Quick Reports'); define ('QR_DEFAULT_TITLE_TAG_TEXT', 'Quick Reports'); -define ('QR_HOME_PAGE_WELCOME_TEXT', 'Welcome to the Quick Reports Tool'); -define ('QR_LOGIN_PAGE_WELCOME_TEXT', 'Welcome to the Quick Reports Tool.'); +define ('QR_HOME_PAGE_WELCOME_TEXT', 'Welcome to the PINES Quick Reports Tool'); +define ('QR_LOGIN_PAGE_WELCOME_TEXT', 'Welcome to the PINES Quick Reports Tool.'); define ('QR_PAGE_FOOTER_TEXT', ''); +//executive reports +define ('QR_EXECUTIVE_REPORTS_ENABLED', true); +define ('QR_EXECUTIVE_REPORTS_START_YEAR', '2016'); +define ('QR_EXECUTIVE_REPORTS_ADDITIONAL_ALLOWED_PERMISSIONS', '141,143'); +define ('QR_EXECUTIVE_REPORTS_DESCRIPTION_URL', ''); +define ('QR_EXECUTIVE_REPORTS_TITLE_TAG_TEXT', 'PINES Executive Reports'); +define ('QR_EXECUTIVE_REPORTS_OUTPUT_HEADER_TITLE', 'PINES Executive Reports'); +define ('QR_EXECUTIVE_REPORTS_CONSORTIUM_COLUMN_HEADING', 'PINES'); +define ('QR_EXECUTIVE_REPORTS_ZERO_VALUE', '---'); +define ('QR_EXECUTIVE_REPORTS_TOTAL_KEY', '__total__'); +define ('QR_EXECUTIVE_REPORTS_SUBREPORT_PADDING', '     '); +define ('QR_EXECUTIVE_REPORTS_MISSING_SUBREPORT_LABEL', '*unknown'); + //Sessions define ('QR_SESSION_TIMEOUT', 120); //minutes diff --git a/controllers/executiveReportGenerator.controller.php b/controllers/executiveReportGenerator.controller.php new file mode 100644 index 0000000..58a5801 --- /dev/null +++ b/controllers/executiveReportGenerator.controller.php @@ -0,0 +1,256 @@ +sanitizeServerQueryString(); + + $report = new executiveReport(); + $executiveReportsList = $report->getExecutiveReportsList(); + $maxReportCount = count($executiveReportsList); + + //url format will be ?[format]/ouID/year/month/reportID/reportID/... + //strip leading / if present, add trailing / if missing, and use explode to generate an array of strings + if (substr($_SERVER['QUERY_STRING'], 0, 1) == '/') $_SERVER['QUERY_STRING']=substr($_SERVER['QUERY_STRING'], 1); + if (substr($_SERVER['QUERY_STRING'],strlen($_SERVER['QUERY_STRING'])-1,1) != '/') $_SERVER['QUERY_STRING'] .= '/'; + $qsParamsArray = explode('/', $_SERVER['QUERY_STRING']); + + if ($qsParamsArray[0] == 'excel') { //download excel + $qsOffset = 1; + $outputFormat = 'excel'; + unset($qsParamsArray[0]); + } + else { + $qsOffset = 0; + $outputFormat = 'html'; + } + + $reportView = new executiveReportsView($outputFormat); + + if (count($qsParamsArray) < (4)) { + $reportView->displayReportHeader(); + new displayMessageView('Invalid parameters.'); + exit; + } + + $buffer = ''; + $numReports = 0; + $system = NULL; + $consortium = NULL; + + $rowData = new stdClass(); + $rowData->system = NULL; + $rowData->consortium = NULL; + + $reportData = new stdClass(); + $reportData->thisMonth = NULL; + $reportData->lastMonth = NULL; + $reportData->lastYear = NULL; + $reportData->reportYear = $qsParamsArray[1 + $qsOffset]; + $reportData->reportMonth = $qsParamsArray[2 + $qsOffset]; + $reportData->reportMonthStr = ($reportData->reportMonth < 10) ? '0'.$reportData->reportMonth : (string)$reportData->reportMonth; + + $orgID = $qsParamsArray[0 + $qsOffset]; + if (!$security->validateInteger($orgID)) { + $reportView->displayReportOutput(); + new displayMessageView('Invalid branch or system specified.'); + exit; + } + + unset($qsParamsArray[0 + $qsOffset]); + unset($qsParamsArray[1 + $qsOffset]); + unset($qsParamsArray[2 + $qsOffset]); + + $invalidDate = false; + if (!$security->validateInteger($reportData->reportYear) || !$security->validateInteger($reportData->reportMonth)) { + $invalidDate = true; + } + else { + switch ($reportData->reportMonth) { + case '1': $reportData->monthName='January'; $reportPriorMonthStr='12'; break; + case '2': $reportData->monthName='February'; $reportPriorMonthStr='01'; break; + case '3': $reportData->monthName='March'; $reportPriorMonthStr='02'; break; + case '4': $reportData->monthName='April'; $reportPriorMonthStr='03'; break; + case '5': $reportData->monthName='May'; $reportPriorMonthStr='04'; break; + case '6': $reportData->monthName='June'; $reportPriorMonthStr='05'; break; + case '7': $reportData->monthName='July'; $reportPriorMonthStr='06'; break; + case '8': $reportData->monthName='August'; $reportPriorMonthStr='07'; break; + case '9': $reportData->monthName='September'; $reportPriorMonthStr='08'; break; + case '10': $reportData->monthName='October'; $reportPriorMonthStr='09'; break; + case '11': $reportData->monthName='November'; $reportPriorMonthStr='10'; break; + case '12': $reportData->monthName='December'; $reportPriorMonthStr='11'; break; + default: $invalidDate = true; break; + } + } + + if ($invalidDate) { + $reportView->displayReportHeader(); + new displayMessageView('Invalid Report Date'); + exit; + } + + $ouInfo = $report->getOUInfo($orgID, QR_QUERY_RETURN_ONE_ROW, true); + if (NULL == $ouInfo) { + $reportView->displayReportHeader(); + new displayMessageView('Invalid branch or system specified.'); + exit; + } + + $reportView->createReportSubHeader($ouInfo, $reportData); + + //get current month, prior month and prior year data for this branch + $yearMonthList = $reportData->reportYear.$reportData->reportMonthStr; //current month + $yearMonthList .= ','.(($reportData->reportMonth==1) ? ($reportData->reportYear-1) : $reportData->reportYear).$reportPriorMonthStr; //last month + $yearMonthList .= ','.($reportData->reportYear-1).$reportData->reportMonthStr; //last year + $data = $report->getExecutiveReportData($ouInfo->id, $yearMonthList); + + //Determine which row of data represents which month and which org type - branch, system, consortium + //Process only the most current rows of data. Records must be returned with "order by org_unit, year_month, desc, create_time desc" + $alreadyProcessed = array(); + foreach ($data as $row) { + if (isset($alreadyProcessed[$row->org_unit.$row->year_month])) continue; + + $alreadyProcessed[$row->org_unit.$row->year_month] = true; + if ($row->year_month == $yearMonthList) { + if ($row->org_unit == $ouInfo->id) + $reportData->thisMonth = $row; + elseif ($row->org_unit == $ouInfo->parent_ou && $ouInfo->ou_type == 3) + $system = $row; + else + $consortium = $row; + } + elseif ($row->org_unit == $ouInfo->id && $row->year_month == ($reportData->reportYear-1).$reportData->reportMonthStr) + $reportData->lastYear = $row; + elseif ($row->org_unit == $ouInfo->id && $row->year_month == (($reportData->reportMonth==1) ? ($reportData->reportYear-1) : $reportData->reportYear).$reportPriorMonthStr) + $reportData->lastMonth = $row; + } + + if (NULL == $reportData->thisMonth) { + $reportView->displayReportOutput(); + new displayMessageView("No data available for $reportData->monthName, $reportData->reportYear"); + exit; + } + + //if no reports are specified default is process all reports + if (isset($qsParamsArray[3 + $qsOffset]) && NULL == $qsParamsArray[3 + $qsOffset]) { + unset($qsParamsArray[3 + $qsOffset]); + foreach ($executiveReportsList as $reportListObj) $qsParamsArray[] = $reportListObj->id; + } + + //process each selected report - one report is one row of output + $reportView->createColumnHeader($ouInfo); + + foreach ($qsParamsArray as $reportID ) { + + if (NULL == $reportID) continue; //skip trailing slash in querystring + + if ($numReports++ > $maxReportCount) { + $reportView->displayReportOutput(); + new displayMessageView('Too many parameters.'); + exit; + } + + //validate report exists + $reportInfo = NULL; + $reportID = strtolower($reportID); //accept querystring parameters in lower or upper case - (e.g.) /p1/ or /P1/ + foreach ($executiveReportsList as $er) { + if ($reportID == strtolower($er->id)) $reportInfo = $er; + } + if (NULL == $reportInfo) continue; //invalid querystring parameter, skip reports that are not defined + + $rowData->format = isset($reportInfo->format) ? $reportInfo->format : NULL; + $reportInfo->ou_type = $ouInfo->ou_type; + + switch ($reportInfo->ou_type) { + case 1: $reportInfo->numColumns=6; break; + case 2: $reportInfo->numColumns=7; break; + default: $reportInfo->numColumns=8; break; + } + + //data may be numeric or an array of json objects for reports that have subreports (group by in sql) + $dataSystem = (!isset($system->$reportID)) ? NULL : json_decode($system->$reportID, false); + $dataConsortium = (!isset($consortium->$reportID)) ? NULL : json_decode($consortium->$reportID, false); + $dataThisMonth = (!isset($reportData->thisMonth->$reportID)) ? NULL : json_decode($reportData->thisMonth->$reportID, false); + $dataLastMonth = (!isset($reportData->lastMonth->$reportID)) ? NULL : json_decode($reportData->lastMonth->$reportID, false); + $dataLastYear = (!isset($reportData->lastYear->$reportID)) ? NULL :json_decode($reportData->lastYear->$reportID, false); + + if (is_array($dataThisMonth) || is_array($dataLastMonth)) { //one of the months is an array of json objects and contains subreports + $dataThisMonthArray=array(); + $dataLastMonthArray=array(); + $dataLastYearArray=array(); + $dataSystemArray=array(); + $dataConsortiumArray=array(); + + + //convert [{"key":"total","value":number},{"key":subreport,"value":numeric},...] to a simple array of key/value pairs and calculate totals + $dataThisMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]=0; + $dataLastMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]=0; + $dataLastYearArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]=0; + $dataSystemArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]=0; + $dataConsortiumArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]=0; + + if (NULL != $dataThisMonth) foreach ($dataThisMonth as $d) {$dataThisMonthArray[$d->key]=$d->value; $dataThisMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]+=$d->value;} + if (NULL != $dataLastMonth) foreach ($dataLastMonth as $d) {$dataLastMonthArray[$d->key]=$d->value; $dataLastMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]+=$d->value;} + if (NULL != $dataLastYear) foreach ($dataLastYear as $d) {$dataLastYearArray[$d->key]=$d->value; $dataLastYearArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]+=$d->value;} + if (NULL != $dataSystem) foreach ($dataSystem as $d) {$dataSystemArray[$d->key]=$d->value; $dataSystemArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]+=$d->value;} + if (NULL != $dataConsortium) foreach ($dataConsortium as $d) {$dataConsortiumArray[$d->key]=$d->value; $dataConsortiumArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]+=$d->value;} + + //first display the total row + $rowData->thisMonth = (!isset($dataThisMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY])) ? NULL : $dataThisMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]; + $rowData->lastMonth = (!isset($dataLastMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY])) ? NULL : $dataLastMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]; + $rowData->lastYear = (!isset($dataLastYearArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY])) ? NULL : $dataLastYearArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]; + $rowData->system = (!isset($dataSystemArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY])) ? NULL : $dataSystemArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]; + $rowData->consortium = (!isset($dataConsortiumArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY])) ? NULL : $dataConsortiumArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]; + + $reportView->createReportRow($reportInfo, $rowData); + + unset($dataThisMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]); + unset($dataLastMonthArray[QR_EXECUTIVE_REPORTS_TOTAL_KEY]); + + //merge and sort subreport data for the current and the prior months - a key/value pair could be missing from one of the months + $dataCombined = array_merge($dataThisMonthArray, $dataLastMonthArray); + uksort($dataCombined, "strcasecmp"); //case insensitive sort + + //display each of the rows of data (subreports) that sum to the total + foreach ($dataCombined as $key => $value) { + $reportInfo->description = QR_EXECUTIVE_REPORTS_SUBREPORT_PADDING.$key; + $rowData->thisMonth = (!isset($dataThisMonthArray[$key])) ? NULL : $dataThisMonthArray[$key]; + $rowData->lastMonth = (!isset($dataLastMonthArray[$key])) ? NULL : $dataLastMonthArray[$key]; + $rowData->lastYear = (!isset($dataLastYearArray[$key])) ? NULL : $dataLastYearArray[$key]; + $rowData->system = (!isset($dataSystemArray[$key])) ? NULL : $dataSystemArray[$key]; + $rowData->consortium = (!isset($dataConsortiumArray[$key])) ? NULL : $dataConsortiumArray[$key]; + $reportView->createReportRow($reportInfo, $rowData); + } + } + else { //data is a numeric value, not json + $rowData->thisMonth = $dataThisMonth; + $rowData->lastMonth = $dataLastMonth; + $rowData->lastYear = $dataLastYear; + $rowData->system = $dataSystem; + $rowData->consortium = $dataConsortium; + $reportView->createReportRow($reportInfo, $rowData); + } + } + + $reportView->displayReportOutput($outputFormat); + } + +} +?> diff --git a/controllers/executiveReportsMenu.controller.php b/controllers/executiveReportsMenu.controller.php new file mode 100644 index 0000000..83e2cfc --- /dev/null +++ b/controllers/executiveReportsMenu.controller.php @@ -0,0 +1,88 @@ +security = new security(); + + switch ($params['action']) { + case NULL: $this->executiveReportsMenu(); break; + case 'reports': $this->createReportListLinks(); break; + case 'report': $this->createReport(); break; + } + } + + + protected function executiveReportsMenu() { + include_once 'models/db.class.php'; + include 'models/report.class.php'; + include 'models/executiveReport.class.php'; + include 'views/baseReport.view.php'; + include 'views/executiveReportsMenu.view.php'; + + $reportObj = new report(); + $orgList = $reportObj->getListDataFromTable((object) array('dataType' => 'org_unit')); + + $executiveReportObj = new executiveReport(); + $executiveReportsList = $executiveReportObj->getExecutiveReportsList(); + + $executiveReports = new executiveReportsView(); + $executiveReports->showExecutiveReportsMenu($orgList, $executiveReportsList); + } + + + protected function createReportListLinks() { + + $params = new stdClass(); + $invalidDate = false; + + if (!isset($_POST['OUList']) || count($_POST['OUList']) == 0) { + $error = new displayMessageView('No locations were specified'); + exit; + } + $orgList = $_POST['OUList']; + + $params->reportYear = $_POST['reportYear']; + $params->reportMonth = $_POST['reportMonth']; + if (!$this->security->validateInteger($params->reportYear) || + !$this->security->validateInteger($params->reportMonth) || + $params->reportMonth<1 || $params->reportMonth>12 || $params->reportYear>date('Y') + ) $invalidDate = true; + + if ($invalidDate) { + new displayMessageView('Invalid Report Date.'); + exit; + } + + unset($_POST['OUList']); + unset($_POST['reportYear']); + unset($_POST['reportMonth']); + //unset($_POST['currentMonthRadio']); + //unset($_POST['executiveReportDate']); + + if (count($_POST) == 0) { + $error = new displayMessageView('No reports were specified'); + exit; + } + $params->reportList = $_POST; //check boxes - save all remaining POST variables + + include_once 'models/db.class.php'; + include 'models/executiveReport.class.php'; + include 'views/baseReport.view.php'; + include 'views/executiveReportsMenu.view.php'; + + $reportObj = new executiveReport(); + $params->orgList = $reportObj->getOUInfo(implode(',', $orgList), QR_QUERY_RETURN_ALL_ROWS); + $executiveReportsList = $reportObj->getExecutiveReportsList(); + + $executiveReports = new executiveReportsView(); + $executiveReports->showReportListLinks($params, $executiveReportsList); + } + +} +?> + diff --git a/css/executiveReportMenu_v1.0.css b/css/executiveReportMenu_v1.0.css new file mode 100644 index 0000000..72728e9 --- /dev/null +++ b/css/executiveReportMenu_v1.0.css @@ -0,0 +1,85 @@ +#executiveReport { + padding: 5px 0 0 10px; +} +#executiveReport table { + border-spacing: 0px; + border-collapse: separate; +} +#executiveReport td { + padding: 0px; + vertical-align: top; +} +#executiveReport input { + font-size: 1.0em; + font-family: Verdana, Arial, Helvetica, sans-serif; +} +#executiveReport textarea { + font-size: 1.0em; + font-family: Verdana, Arial, Helvetica, sans-serif; + width: 98%; + min-width: 500px; + height: 60px; +} +#executiveReport select { + font-size: 1.0em; + font-family: Verdana, Arial, Helvetica, sans-serif; +} +.executiveReportMenu { + outline-style: solid; + outline-color: #15513d; + outline-width: 2px; + padding: 5px; +} +.executiveReportSubMenu { + outline-style: solid; + outline-color: #999; + outline-width: 1px; + padding: 0px; + width: 260px; + padding: 5px 0px 5px 5px; +} +.executiveReportSubMenu td { + font-size: 1.0em !important; +} +.executiveReportSubMenu input { + margin-top: 4px; + vertical-align: sub +} +.executiveReportTopMenu { + height: 155px; +} +.executiveReportSubMenuRow1 { + height: 130px; +} +.executiveReportSubMenuRow2 { + height: 170px; +} +#executiveReportDate { + width: 115px; + margin: 5px 0 0 25px; +} +.executiveReportTypeLink, +#executiveReportTypeLink:link, +#executiveReportTypeLink:visited, +#executiveReportTypeLink:hover, +#executiveReportDefinitionsLink:active { + color: #0066ff; + font-size: 9pt; + text-decoration: none +} +#executiveReportDefinitionsLink, +#executiveReportDefinitionsLink:link, +#executiveReportDefinitionsLink:visited, +#executiveReportDefinitionsLink:hover, +#executiveReportDefinitionsLink:active { + color: #00f; + text-decoration: none +} +.executiveReportCheckbox {} +.width10 { + width: 10px; +} +.width20 { + width: 20px; +} + diff --git a/css/executiveReport_v1.0.css b/css/executiveReport_v1.0.css new file mode 100644 index 0000000..e5585f9 --- /dev/null +++ b/css/executiveReport_v1.0.css @@ -0,0 +1,68 @@ +* {} +html, +body, +td { + padding:0; + margin-left: 30px; + height: 100%; + background-color:#fff; + font-size:11pt; + font-family:Arial, Helvetica, Verdana, sans-serif; +} + +#logo { + background: url(../images/PINESLogo.gif) no-repeat; + width:300px; + height:60px; + position:relative; +} + +#subHeaderLeft { + min-width:300px; +} + +.contentTable { + border-spacing:0px; + border-collapse:collapse; +} + +.contentTable td { + border:1px solid #888; + padding:0 5px 0 5px; +} + +.reportHeader { + font-weight:bold; + font-size:1.2em +} + +.reportSubHeader { + font-weight:normal; +} + +.rowLabel { + font-weight:bold; + vertical-align:top; + text-align:left; +} + +.columnLabel { + font-weight:bold; + vertical-align:top; + text-align:center; +} + +.errorText { + color:#f00; +} + +.negativeValue { + color:#f00 +} + +.zeroValue {} + +.zeroValueBackground { + background-color:#ccc +} + diff --git a/executiveReport.php b/executiveReport.php new file mode 100644 index 0000000..277c4e3 --- /dev/null +++ b/executiveReport.php @@ -0,0 +1,8 @@ + + diff --git a/lib/site_v1.5.js b/lib/site_v1.5.js new file mode 100644 index 0000000..4cd3f26 --- /dev/null +++ b/lib/site_v1.5.js @@ -0,0 +1,297 @@ +var lastExpandedGroupName; +var lastSelectAll = false; +var lastSelectAllState = []; + +function toggleReportShowHide(className, labelName) { + var list = document.getElementsByClassName(className); + if (list.length>0) { + for (var i = 0; i < list.length; i++) { + if (reportFiltersState[className]) { + list[i].style.display="table-row"; + } + else { + list[i].style.display="none"; + } + } + } + reportFiltersState[className]=!reportFiltersState[className]; //toggle + if (reportFiltersState[className]) + document.getElementById(labelName).innerHTML="+Show"; + else + document.getElementById(labelName).innerHTML="-Hide"; +} + +function toggleRealRelativeDate(dateTypeElement, spanTagReal, spanTagRelative) { + if (dateTypeElement.value == "real") { + document.getElementById(spanTagReal).style.display = ""; + document.getElementById(spanTagRelative).style.display = "none"; + } + else { + document.getElementById(spanTagReal).style.display = "none"; + document.getElementById(spanTagRelative).style.display = ""; + } +} + +function toggleCollapsibleSection(group) { + var rows; + var hide; + var spanPlusMinus = group + "_plus"; + var rowName = group + "_row"; + + rows = document.getElementsByClassName(rowName); + if (!rows[0].className.match(/(?:^|\s)hideRow(?!\S)/) ) { + hide = true; + lastExpandedGroupName = undefined; + document.getElementById(spanPlusMinus).innerHTML = "+"; + } + else { + hide = false; + if (typeof lastExpandedGroupName != 'undefined' && lastExpandedGroupName != group) toggleCollapsibleSection(lastExpandedGroupName); + lastExpandedGroupName = group; + document.getElementById(spanPlusMinus).innerHTML = "-"; + } + for(var i=0; i"; + } + document.getElementById(span).innerHTML=html; +} + +function selectAllMultiselect(listName) { + var list = document.getElementById(listName); + for(var i=0; i thisMonth-1 && reportYearIndex == 0) return; + if (reportMonth > 11) { + reportMonth = 0; + reportYearIndex -= 1; + } + else if (reportMonth < 0) { + reportMonth = 11; + reportYearIndex += 1; + } + } + document.getElementById("reportMonth").selectedIndex = reportMonth; + document.getElementById("reportYear").selectedIndex = reportYearIndex; +} + +function submitReport(action) { + + var userInputs; + var dateTypeName; + var colName; + + if (document.getElementById("name").value.length==0) { + alert("Please enter a report name."); + return false; + } + + if (action == 'save') { + return true; + } + + userInputs = document.getElementsByClassName('userInput'); + for (var i=0; i 12) return false; + if (d < 1 || d > 31) return false; + if ((m == 4 || m == 6 || m == 9 || m == 11) && d > 30) return false; + if (m ==2) { + if ((y % 400) == 0 || ((y % 4) == 0 && (y % 100) != 0)) { //leap year + if (d > 29) return false; + } + else { //not leap year + if (d > 28) return false; + } + } + return true; +} + +var win1=null; +function openWindow(html) { + if (win1 != null && win1.open) win1.close(); + win1=window.open('','win1','height=250,width=800,toolbar=no,location=no,menubar=no,titlebar=no,status=no,scrollbars=yes,resizable=yes,modal=yes'); + win1.document.write("
"+html+"
"); +} + diff --git a/models/executiveReport.class.php b/models/executiveReport.class.php new file mode 100644 index 0000000..205d5ff --- /dev/null +++ b/models/executiveReport.class.php @@ -0,0 +1,55 @@ +executeQuery($query, NULL, $rowsReturnedType); + } + catch (PDOException $e) { + $this->handleDatabaseErrors($e); + } + } + + + public function getExecutiveReportData($orgID, $yearMonthList) { + //get the data for the report output + //must order by org_unit,year_month desc, create_time desc + //use select * - allows reports to be added without modifying this query + try { + $query = ' + select * + from '.QR_DB_SCHEMA.".executive_reports_data where year_month in ($yearMonthList) + and org_unit in (:orgID,(select parent_ou from actor.org_unit where id=:orgID), + (select id from actor.org_unit where ou_type=1)) + order by org_unit,year_month desc, create_time desc"; + $params = array('orgID'=>$orgID); + return $this->executeQuery($query, $params, QR_QUERY_RETURN_ALL_ROWS); + } + catch (PDOException $e) { + $this->handleDatabaseErrors($e); + } + } + +} +?> + diff --git a/views/executiveReportGenerator.view.php b/views/executiveReportGenerator.view.php new file mode 100644 index 0000000..efa832a --- /dev/null +++ b/views/executiveReportGenerator.view.php @@ -0,0 +1,252 @@ +lastCategory = NULL; + $this->reportFirstRow = true; + $this->outputFormat = $outputFormat; + $this->createReportHeader(); + } + + + private function createReportHeader() { + + $sitePath = $this->outputFormat=='excel' ? ($_SERVER['SERVER_PORT']==443 ? 'https://':'http://') . $_SERVER['SERVER_NAME'] . QR_SITE_ROOT : ''; + $this->reportHeader = ' + + + + + '.QR_EXECUTIVE_REPORTS_TITLE_TAG_TEXT.' + + + + + + + + + + + + +
+ + + '; + return; + } + + + public function createReportSubHeader($ouInfo, $reportDate) { + + $buffer = ' +
'.QR_EXECUTIVE_REPORTS_OUTPUT_HEADER_TITLE.'
'; + switch ($ouInfo->ou_type) { + case 1: $buffer .= 'Consortium: '; break; + case 2: $buffer .= 'System: '; break; + default: $buffer .= 'Branch: '; break; + } + + //subHeader (report name, org unit, date) followed by column headings + $buffer .= ' + '.$ouInfo->name.'
Date:'.$reportDate->monthName.', '.$reportDate->reportYear.'
+
 
+ + '; + $this->reportOutput .= $buffer; + } + + + public function createColumnHeader($ouInfo){ + + $buffer = ' + + + + + + '; + + //add columns for system and/or consortium + if ($ouInfo->ou_type == 3) $buffer .= ''; + if ($ouInfo->ou_type >= 2) $buffer .= ''; + $this->reportOutput .= $buffer; + } + + + public function displayReportHeader() { + + if ($this->outputFormat == 'excel') { + header("Content-Disposition: attachment; filename=\"executive_report.xls\""); + header("Content-Type: application/vnd.ms-excel"); + } + echo $this->reportHeader; + } + + + public function displayReportOutput($outputFormat = NULL) { + + $this->displayReportHeader($outputFormat); + echo $this->reportOutput; + $verID='3fca9f8aa945f04e090b258b2fcf10e4'; + echo "
CategoryReportCurrent
Month
Previous
Month
% Change
from
Previous
Month
% Change
from
Previous
Year
% of
System
Total
% of
'.QR_EXECUTIVE_REPORTS_CONSORTIUM_COLUMN_HEADING.'
Total

"; + } + + + public function createReportRow($reportInfo, $rowData) { + + //setup the data display variables + $buffer = NULL; + $thisMonth = $rowData->thisMonth; + $lastMonth = $rowData->lastMonth; + $yearlyChange = NULL; + $monthlyChange = NULL; + $percentSystem = NULL; + $percentConsortium = NULL; + $thisMonthBackgroundClass = NULL; + $lastMonthBackgroundClass = NULL; + $yearlyChangeBackgroundClass = NULL; + $monthlyChangeBackgroundClass = NULL; + $percentSystemBackgroundClass = NULL; + $percentConsortiumBackgroundClass = NULL; + + if (0 == $rowData->thisMonth) $rowData->thisMonth = NULL; + if (0 == $rowData->lastMonth) $rowData->lastMonth = NULL; + + if (NULL == $rowData->thisMonth) { + $thisMonth = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $thisMonthBackgroundClass= ' class="zeroValueBackground" '; + + $yearlyChange = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $yearlyChangeBackgroundClass = ' class="zeroValueBackground" '; + + $monthlyChange = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $monthlyChangeBackgroundClass = ' class="zeroValueBackground" '; + + $rowData->system = NULL; + $percentSystem = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $percentSystemBackgroundClass = ' class="zeroValueBackground" '; + + $rowData->consortium = NULL; + $percentConsortium = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $percentConsortiumBackgroundClass = ' class="zeroValueBackground" '; + } + + if (NULL == $rowData->lastMonth) { + $lastMonth = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $lastMonthBackgroundClass= ' class="zeroValueBackground" '; + $monthlyChange = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $monthlyChangeBackgroundClass = ' class="zeroValueBackground" '; + } + + if (NULL == $rowData->lastYear) { + $yearlyChange = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $yearlyChangeBackgroundClass= ' class="zeroValueBackground" '; + } + + if (NULL == $monthlyChange) { + if ($rowData->thisMonth == $rowData->lastMonth) { + $monthlyChange = '0%'; + } + else { + $monthlyChange = number_format(100 * ($rowData->thisMonth - $rowData->lastMonth) / $rowData->lastMonth) . '%'; + if ($monthlyChange == '-0%') $monthlyChange = '0%'; + if (substr($monthlyChange, 0, 1) == '-') $monthlyChange = "$monthlyChange"; + } + } + + if (NULL == $yearlyChange) { + if ($rowData->thisMonth == $rowData->lastYear) { + $yearlyChange = '0%'; + } + else { + $yearlyChange = number_format(100 * ($rowData->thisMonth - $rowData->lastYear) / $rowData->lastYear) . '%'; + if ($yearlyChange == '-0%') $yearlyChange = '0%'; + if (substr($yearlyChange, 0, 1) == '-') $yearlyChange = "$yearlyChange"; + } + } + + //create a new row. Do not repeat the category in the first column. + //Blank row between categories. No blank row after the column headers. + $buffer .= ''; + if ($reportInfo->category != $this->lastCategory) { + if ($this->reportFirstRow) + $this->reportFirstRow = false; + else { + //new category so this row will become the blank row between categories + $buffer .= ''; + for ($i=1; $i<=$reportInfo->numColumns-1; $i++) $buffer .= ' '; + $buffer .= ''; + } + $buffer .= $reportInfo->category; //column 1 + $this->lastCategory = $reportInfo->category; + } + + //if format is set to currency display as currency, else default to integer + $numDecimals = (isset($rowData->format) && $rowData->format == 'currency') ? 2 : 0; + if (NUll != $rowData->thisMonth) $thisMonth = ($rowData->format == 'currency' ? '$' : '') . number_format($rowData->thisMonth, $numDecimals); + if (NUll != $rowData->lastMonth) $lastMonth = ($rowData->format == 'currency' ? '$' : '') . number_format($rowData->lastMonth, $numDecimals); + + //display the remainig data columns + $buffer .= ''.((QR_EXECUTIVE_REPORTS_SUBREPORT_PADDING == $reportInfo->description) ? QR_EXECUTIVE_REPORTS_SUBREPORT_PADDING.QR_EXECUTIVE_REPORTS_MISSING_SUBREPORT_LABEL : $reportInfo->description).''; + $buffer .= "".$thisMonth.''; + $buffer .= "".$lastMonth.''; + $buffer .= "$monthlyChange"; + $buffer .= "$yearlyChange"; + + //% of system column + if ($reportInfo->ou_type == 3) { + if (NULL == $percentSystem) { + if (NULL == $rowData->system || NULL == $rowData->thisMonth) { + $percentSystem = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $percentSystemBackgroundClass = ' class="zeroValueBackground" '; + } + elseif ($rowData->thisMonth == $rowData->system) { + $percentSystem = '100%'; + } + else { + $percentSystem = number_format(100 * $rowData->thisMonth / $rowData->system) . '%'; + if ($percentSystem == '-0%') $percentSystem = '0%'; + if (substr($percentSystem, 0, 1) == '-') $percentSystem = "$percentSystem"; + } + } + $buffer .= "$percentSystem"; + } + + // % of consortium column + if ($reportInfo->ou_type >= 2) { + if (NULL == $percentConsortium) { + if (NULL == $rowData->consortium) { + $percentConsortium = ''.QR_EXECUTIVE_REPORTS_ZERO_VALUE.''; + $percentConsortiumBackgroundClass = ' class="zeroValueBackground" '; + } + elseif ($rowData->thisMonth == $rowData->consortium) { + $percentConsortium = '100%'; + } + else { + $percentConsortium = number_format(100 * $rowData->thisMonth / $rowData->consortium) . '%'; + if ($percentConsortium == '-0%') $percentConsortium = '0%'; + if (substr($percentConsortium, 0, 1) == '-') $percentConsortium = "$percentConsortium"; + } + } + $buffer .= "$percentConsortium"; + } + + $buffer .= ''; //complete the row + $this->reportOutput .= $buffer; + return; + } + +} +?> + diff --git a/views/executiveReportsMenu.view.php b/views/executiveReportsMenu.view.php new file mode 100644 index 0000000..743ad68 --- /dev/null +++ b/views/executiveReportsMenu.view.php @@ -0,0 +1,214 @@ +orgList)>1) ? 's' : ''; + echo "Please click on the links below to view your report$pluralSuffix
"; + + //truncate report list if all reports, else insert / as delimiter + $params->reportList = (count($params->reportList) == count($executiveReportsList)) ? '' : implode('/', array_keys($params->reportList)); + + foreach ($params->orgList as $orgListObj) { + echo '
'; + if ($orgListObj->ou_type == 2) + echo '
     '; + elseif ($orgListObj->ou_type == 3) + echo '          '; + echo $orgListObj->shortname,' '; + echo '  HTML'; + echo ' |'; + echo ' Excel'; + } + + echo '

'; + } + + + public function showExecutiveReportsMenu($orgList, $executiveReportsList) { + + ?> +
+ + + + + + + +
Create Your Executive Snapshot Report:
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
1. Choose Location(s)
+ createOrgUnitSelect($orgList, 'OUList', false); ?> +
+
+ + + + + +
2. Choose Month / Year

+ + + + + + + +
+
+ + + + + + + + + + + + + '; + $newColumn = true; + if (QR_EXECUTIVE_REPORTS_MENU_COLUMNS == $numColumns) { + $numColumns = 1; + echo ''; + } + else + $numColumns++; + } + + if ($newColumn) { + $newColumn = false; + echo ''; + echo ' + + + + +
3. Choose Report Data  Select All + Report Data Definitions'; ?> +
+ +
+ + category); + + if (NULL == $currentCategory) { //first row, first column + echo '',$er->category,'  Select All'; + echo '
', $er->name; + $categoryList[] = $cleanCategory; + } + + if ($er->category == $currentCategory) + echo '
', $er->name; + else { //new category + if ($er->category != $currentCategory && NULL != $currentCategory) { + echo '
'; + echo ' +
'; + echo '',$er->category,'  Select All'; + echo '
', $er->name; + $categoryList[] = $cleanCategory; + } + } + $currentCategory = $er->category; + } + ?> +
+
+
+ +
+
+   +
+
+ +
+
+
+ + + + diff --git a/views/homePage.view.php b/views/homePage.view.php index 939a4e2..5b17bb6 100644 --- a/views/homePage.view.php +++ b/views/homePage.view.php @@ -22,7 +22,7 @@ class homePageView {   My Quick Reports
- View reports you have already run, or that you have scheduled. + View reports you have already run or that you have scheduled. diff --git a/views/page.view.php b/views/page.view.php index a3abc01..0bcb3c1 100644 --- a/views/page.view.php +++ b/views/page.view.php @@ -16,7 +16,9 @@ class pageView { <?php echo QR_DEFAULT_TITLE_TAG_TEXT; ?> - + '; ?> + +
@@ -92,8 +94,9 @@ class pageView {