Adding Chris DeBellis's Executive Reports work
authorChris Sharp <csharp@georgialibraries.org>
Sun, 15 Jan 2017 18:30:39 +0000 (13:30 -0500)
committerChris Sharp <csharp@georgialibraries.org>
Sun, 15 Jan 2017 18:31:32 +0000 (13:31 -0500)
Signed-off-by: Chris Sharp <csharp@georgialibraries.org>
14 files changed:
config/app.config.php
config/executiveReports.config.php [new file with mode: 0644]
config/production.config.php
controllers/executiveReportGenerator.controller.php [new file with mode: 0644]
controllers/executiveReportsMenu.controller.php [new file with mode: 0644]
css/executiveReportMenu_v1.0.css [new file with mode: 0644]
css/executiveReport_v1.0.css [new file with mode: 0644]
executiveReport.php [new file with mode: 0644]
lib/site_v1.5.js [new file with mode: 0644]
models/executiveReport.class.php [new file with mode: 0644]
views/executiveReportGenerator.view.php [new file with mode: 0644]
views/executiveReportsMenu.view.php [new file with mode: 0644]
views/homePage.view.php
views/page.view.php

index da8f77f..44eca97 100644 (file)
@@ -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("/\\\\/", '/&#10;/', '/&#13;/', '/\n/', '/\r/', '/\t/', '/\v/', '/\f/', "/'/", '/"/', '/&#34;/')));
 define ('QR_DEFAULT_ESCAPE_REPLACEMENTS', serialize(array('\\\\\\', '\n', '\r', '\n', '\r', '\t', '\v', '\f', '&apos;', '\"', '\"')));
-?>
\ No newline at end of file
+?>
diff --git a/config/executiveReports.config.php b/config/executiveReports.config.php
new file mode 100644 (file)
index 0000000..a60381b
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+$report[] = (object) array (
+               'id' => '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');
+
+?>
index 9744bf8..e0ceac0 100644 (file)
@@ -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', '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;');
+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 (file)
index 0000000..58a5801
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+if (isset($_SERVER['SERVER_NAME']) && ($_SERVER['SERVER_NAME']=='localhost' || $_SERVER['SERVER_NAME']=='dev'))
+       include 'config/dev.config.php';
+else
+       include 'config/production.config.php';
+include 'config/app.config.php';
+include 'models/db.class.php';
+include 'models/executiveReport.class.php';
+include 'views/executiveReportGenerator.view.php';
+include 'models/security.class.php';
+include 'views/displayMessage.view.php';
+
+class executiveReportGenerator {
+       
+       public function __construct() {
+
+               if (QR_PGSQL_USE_OPENSRF_XML_CONFIG || (QR_SESSIONS_IN_MEMCACHE && QR_MEMCACHE_USE_OPENSRF_XML_CONFIG)) {
+                       include 'models/openilsConfig.class.php';
+                       new openilsConfig();    //parse openils xml config file
+               }
+               
+               $security = new security();
+               $security->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 (file)
index 0000000..83e2cfc
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+
+//This class allows the user to select reports to view
+class executiveReportsMenuController {
+       
+       protected $security;
+       
+       public function __construct($params=NULL) {
+               
+               $this->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 (file)
index 0000000..72728e9
--- /dev/null
@@ -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 (file)
index 0000000..e5585f9
--- /dev/null
@@ -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 (file)
index 0000000..277c4e3
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+//This class generates a report output 
+//There is no session / login security. Anyone can access a report from a shared URL.
+
+include 'controllers/executiveReportGenerator.controller.php';
+new executiveReportGenerator();
+?>
+
diff --git a/lib/site_v1.5.js b/lib/site_v1.5.js
new file mode 100644 (file)
index 0000000..4cd3f26
--- /dev/null
@@ -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<rows.length; i++) {
+               if (hide)               
+                       rows[i].className += " hideRow ";
+               else
+                       rows[i].className = rows[i].className.replace("hideRow" , "");
+       }
+}
+
+function confirmDeleteReport(id, name, action, adminView, recurs, url) {
+       var message;
+       message = action + " the report "+name+"?";
+       if (recurs==1) message = message + "\r\nAll future recurrences will also be canceled.";
+       if (confirm(message)) {
+               url = url + "c/" + id + "/";
+               if (adminView) url = url + "a/";
+               window.location.href = url; 
+               return true;
+       } 
+       return false;
+}
+
+function showSelectChoices(list, span) {
+       var html='';
+       for (var i=0; i<list.length; i++) {
+               if (list[i].selected) html = html + list[i].label.trim() + "&nbsp;&nbsp;<br>";
+       }
+       document.getElementById(span).innerHTML=html;
+}
+
+function selectAllMultiselect(listName) {
+       var list = document.getElementById(listName);
+       for(var i=0; i<list.options.length; i++) {
+               list.options[i].selected = true; 
+       }
+       showSelectChoices(list, listName+"_selected");
+}
+
+function toggleAllCheckboxes(className) {
+       
+       if (className == "executiveReportCheckbox") {
+               toggleState = !lastSelectAll;
+               lastSelectAll = !lastSelectAll;
+               
+               for (var key in lastSelectAllState) {
+                       lastSelectAllState[key] = !lastSelectAllState[key]; 
+               }
+       }
+       else {
+               lastSelectAllState[className] = !lastSelectAllState[className];
+               toggleState = lastSelectAllState[className];
+       }
+       
+       var list = document.getElementsByClassName(className);
+       for (var i=0; i<list.length; i++) {
+               list[i].checked = toggleState;
+       }
+       
+       var newText = (toggleState) ? 'Clear All' : 'Select All';
+       if (className == "executiveReportCheckbox") document.getElementById("selectAllReportsLink").text = newText;
+       
+       for (var key in lastSelectAllState) {
+               if (className == "executiveReportCheckbox" || className == key) {
+                       document.getElementById(key+"SelectAllReportsLink").text = newText;
+               }
+       }
+}
+
+function submitExecutiveReport() {
+       var numChecked = 0;
+       var reportList = document.getElementsByClassName("executiveReportCheckbox");
+       for (var i=0; i<reportList.length; i++) {
+               if (reportList[i].checked) {
+                       numChecked++;
+                       break;
+               }
+       }
+       if (0 == numChecked) {
+               alert("Please select one or more reports.");
+               return false;
+       }
+       return true;
+}
+
+function setExecutiveReportDate(offset) {
+       var today=new Date();
+       var thisYear=today.getFullYear();
+       var thisMonth=today.getMonth();
+       var reportMonth;
+       var reportYear;
+       var reportYearIndex;
+       
+       if (0 == offset) {
+               if (0 == thisMonth) {
+                       reportMonth = 11;
+                       reportYear = thisYear - 1;
+               }
+               else {
+                       reportMonth = thisMonth - 1;
+                       reportYearIndex = 0;
+               }
+       }
+       else {
+               reportMonth = document.getElementById("reportMonth").selectedIndex + offset;
+               reportYearIndex = document.getElementById("reportYear").selectedIndex;
+               if (reportMonth < 0 && reportYearIndex == document.getElementById("reportYear").length-1) return;
+               if (reportMonth > 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<userInputs.length; i++) {
+               if (userInputs[i].classList.contains('userText') && userInputs[i].value=="") {
+                       if (userInputs[i].name.indexOf("_") != -1)
+                               colName = columnNames[userInputs[i].name.substring(0,userInputs[i].name.indexOf("_"))];
+                       else
+                               colName = columnNames[userInputs[i].name];
+                       alert("Please enter a value for " + colName);
+                       userInputs[i].focus();
+                       return false;
+               }
+               else if (userInputs[i].classList.contains('userInteger') && (userInputs[i].value=="" || !validateInteger(userInputs[i].value))) {
+                       if (userInputs[i].name.indexOf("_") != -1)
+                               colName = columnNames[userInputs[i].name.substring(0,userInputs[i].name.indexOf("_"))];
+                       else
+                               colName = columnNames[userInputs[i].name];
+                       alert("Please enter an integer for " + colName);
+                       userInputs[i].focus();
+                       return false;
+               }
+               else if (userInputs[i].classList.contains('userDate')) {
+                       dateTypeName = userInputs[i].name.replace("_date","_type");
+                       if (document.getElementById(dateTypeName).value == 'real' && !validDate(userInputs[i].value) ) {
+                               alert("You must specify a valid date for " + columnNames[userInputs[i].name.substring(0,userInputs[i].name.indexOf("_"))]);
+                               userInputs[i].focus();
+                               return false;
+                       }
+               }
+               else if (userInputs[i].classList.contains('userSelect') && userInputs[i].selectedIndex==-1) {
+                       alert("Please select a value for " + columnNames[userInputs[i].name.replace("[]","")]);
+                       userInputs[i].focus();
+                       return false;                   
+               }
+       }
+       
+       if (!document.getElementById("intervalRadioOnce").checked && !document.getElementById("intervalRadioRecur").checked) {
+           alert("Please select the recurrence interval.");
+           return false;
+       }
+       if (document.getElementById("intervalRadioRecur").checked && (document.getElementById("interval").selectedIndex==0 || document.getElementById("intervalPeriod").selectedIndex==0)) {
+               alert("Please specify the recurrence frequency.");
+               return false;
+       }
+       
+       if (!document.getElementById("runTimeRadioASAP").checked && !document.getElementById("runTimeRadioScheduled").checked) {
+           alert("Please select the run time.");
+           return false;
+       }
+       if (document.getElementById("runTimeRadioScheduled").checked) {
+               if (document.getElementById("runDate").value=="") {
+                       alert("Please specify the scheduled run date.");
+                       return false;
+               }
+               if (!validDate(runDate.value)) {
+                       alert("Please specify a valid scheduled run date.");
+                       return false;                   
+               }
+       }
+       
+       if (!document.getElementById("excelOutput").checked && !document.getElementById("csvOutput").checked && !document.getElementById("htmlOutput").checked) {
+               alert("Please choose an output format.");
+               return false;
+       }
+       
+       return true;
+}
+
+function setDaysLabel(elem, spanTag){
+       if (elem.value == -1)
+               document.getElementById(spanTag).innerHTML = "Day";
+       else
+               document.getElementById(spanTag).innerHTML = "Days";
+}
+
+function validateInteger(n) {
+       if (isNaN(parseFloat(n)) || !isFinite(n)) return false;
+       return true;
+}
+
+function emptyString(s) {
+       return (s=="");
+}
+
+function validDate(date) {
+       var matches = /^(\d{2})[-\/](\d{2})[-\/](\d{4})$/.exec(date);
+    if (matches == null) return false;
+    var d = matches[2];
+    var m = matches[1];
+    var y = matches[3];
+    if (!validateInteger(y) || !validateInteger(m) || !validateInteger(d)) return false;
+    if (m < 1 || m > 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("<pre>"+html+"</pre>");
+}
+
diff --git a/models/executiveReport.class.php b/models/executiveReport.class.php
new file mode 100644 (file)
index 0000000..205d5ff
--- /dev/null
@@ -0,0 +1,55 @@
+<?php 
+class executiveReport extends db
+{
+       
+       public function __construct() {
+               parent::__construct();                  
+       }
+               
+       public function getExecutiveReportsList() {
+               //Define all of the executive reports
+               
+               $report = array();      
+               include 'config/executiveReports.config.php';
+               return $report;
+       }
+
+
+       function getOUInfo($OUList, $rowsReturnedType, $includeName=false) {
+               
+               $name = ($includeName) ? 'name,' : '';
+               try {
+                       //Put library system at the top
+                       $query = "
+                       select id, shortname, ou_type, parent_ou, $name case when ou_type=1 then 1 else 0 end as isSystem
+                       from actor.org_unit where id in ($OUList) order by isSystem desc, shortname";
+                       return $this->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 (file)
index 0000000..efa832a
--- /dev/null
@@ -0,0 +1,252 @@
+<?php 
+class executiveReportsView {
+       private $lastCategory;
+       private $reportHeader;
+       private $reportOutput;
+       private $outputFormat;
+       private $resourcesPath;
+       private $reportFirstRow;
+       
+       public function __construct($outputFormat = NULL) {
+               
+               //automatically display the header in case we abort because of an error
+               $this->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 = '
+               <!doctype html>
+               <html>
+               <head>
+               <meta charset="utf-8">
+               <title>'.QR_EXECUTIVE_REPORTS_TITLE_TAG_TEXT.'</title>
+               
+               <link type="text/css" href="'.$sitePath.'css/executiveReport_v1.0.css" rel="stylesheet">
+               
+               </head>
+               <body>
+               
+               <table cellpadding="0" cellspacing="0" border="0">
+               <tr>
+               <td id="subHeaderLeft">
+               <table cellpadding="0" cellspacing="0" border="0" >
+               <tr><td class="reportHeader" colspan="2">'.QR_EXECUTIVE_REPORTS_OUTPUT_HEADER_TITLE.'</td></tr>
+               ';              
+               return;
+       }
+
+
+       public function createReportSubHeader($ouInfo, $reportDate) {
+                               
+               $buffer = '<tr><td class="reportSubHeader">';
+               switch ($ouInfo->ou_type) {
+                       case 1: $buffer .= 'Consortium:&nbsp;'; break;
+                       case 2: $buffer .= 'System:&nbsp;'; break;
+                       default: $buffer .= 'Branch:&nbsp;'; break;
+               }
+
+               //subHeader (report name, org unit, date) followed by column headings
+               $buffer .= '
+               </td><td class="reportSubHeader">'.$ouInfo->name.'</td></tr>
+               <tr><td class="reportSubHeader">Date:</td><td class="reportSubHeader">'.$reportDate->monthName.', '.$reportDate->reportYear.'</td></tr></table>
+               </td>
+               <td id="logo"></td>
+               </tr>
+               <tr><td colspan="2">&nbsp;</td></tr>
+               <tr><td colspan="2">
+               <table class="contentTable">
+               <tr>';
+               $this->reportOutput .= $buffer;
+       }
+
+
+       public function createColumnHeader($ouInfo){
+               
+               $buffer = '
+               <td class="columnLabel">Category</td>
+               <td class="columnLabel">Report</td>
+               <td class="columnLabel">Current<br>Month</td>
+               <td class="columnLabel">Previous<br>Month</td>
+               <td class="columnLabel">% Change<br>from<br>Previous<br>Month</td>
+               <td class="columnLabel">% Change<br>from<br>Previous<br>Year</td>';
+               
+               //add columns for system and/or consortium
+               if ($ouInfo->ou_type == 3) $buffer .= '<td class="columnLabel">% of<br>System<br>Total</td>';
+               if ($ouInfo->ou_type >= 2) $buffer .= '<td class="columnLabel">% of<br>'.QR_EXECUTIVE_REPORTS_CONSORTIUM_COLUMN_HEADING.'<br>Total</td>';
+               $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 "</table></table><!--$verID--><br></body></html>";
+       }       
+       
+       
+       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 = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                       $thisMonthBackgroundClass= ' class="zeroValueBackground" ';
+       
+                       $yearlyChange = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                       $yearlyChangeBackgroundClass = ' class="zeroValueBackground" ';
+       
+                       $monthlyChange = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                       $monthlyChangeBackgroundClass = ' class="zeroValueBackground" ';
+       
+                       $rowData->system = NULL;
+                       $percentSystem = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                       $percentSystemBackgroundClass = ' class="zeroValueBackground" ';
+       
+                       $rowData->consortium = NULL;
+                       $percentConsortium = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                       $percentConsortiumBackgroundClass = ' class="zeroValueBackground" ';
+               }
+       
+               if (NULL == $rowData->lastMonth) {
+                       $lastMonth = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                       $lastMonthBackgroundClass= ' class="zeroValueBackground" ';
+                       $monthlyChange = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                       $monthlyChangeBackgroundClass = ' class="zeroValueBackground" ';
+               }
+
+               if (NULL == $rowData->lastYear) {
+                       $yearlyChange = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                       $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 = "<span class=\"negativeValue\">$monthlyChange</span>";
+                       }
+               }
+       
+               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 = "<span class=\"negativeValue\">$yearlyChange</span>";
+                       }
+               }
+       
+               //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 .= '<tr><td class="rowLabel">';
+               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 .= '</td>';
+                               for ($i=1; $i<=$reportInfo->numColumns-1; $i++) $buffer .= '<td>&nbsp;</td>';
+                               $buffer .= '<tr><td class="rowLabel">';
+                       }
+                       $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 .= '</td><td>'.((QR_EXECUTIVE_REPORTS_SUBREPORT_PADDING == $reportInfo->description) ? QR_EXECUTIVE_REPORTS_SUBREPORT_PADDING.QR_EXECUTIVE_REPORTS_MISSING_SUBREPORT_LABEL : $reportInfo->description).'</td>';
+               $buffer .= "<td align=\"right\" $thisMonthBackgroundClass>".$thisMonth.'</td>';
+               $buffer .= "<td align=\"right\" $lastMonthBackgroundClass>".$lastMonth.'</td>';
+               $buffer .= "<td align=\"right\" $monthlyChangeBackgroundClass>$monthlyChange</td>";
+               $buffer .= "<td align=\"right\" $yearlyChangeBackgroundClass>$yearlyChange</td>";
+       
+               //% of system column
+               if ($reportInfo->ou_type == 3) {
+                       if (NULL == $percentSystem)     {
+                               if (NULL == $rowData->system || NULL == $rowData->thisMonth) {
+                                       $percentSystem = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                                       $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 = "<span class=\"negativeValue\">$percentSystem</span>";
+                               }
+                       }
+                       $buffer .= "<td align=\"right\" $percentSystemBackgroundClass>$percentSystem</td>";
+               }
+       
+               // % of consortium column
+               if ($reportInfo->ou_type >= 2) {
+                       if (NULL == $percentConsortium) {
+                               if (NULL == $rowData->consortium) {
+                                       $percentConsortium = '<span class="zeroValue">'.QR_EXECUTIVE_REPORTS_ZERO_VALUE.'</span>';
+                                       $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 = "<span class=\"negativeValue\">$percentConsortium</span>";
+                               }
+                       }
+                       $buffer .= "<td align=\"right\" $percentConsortiumBackgroundClass>$percentConsortium</td>";
+               }
+               
+               $buffer .= '</tr>';             //complete the row
+               $this->reportOutput .= $buffer;
+               return;
+       }
+
+}
+?>
+
diff --git a/views/executiveReportsMenu.view.php b/views/executiveReportsMenu.view.php
new file mode 100644 (file)
index 0000000..743ad68
--- /dev/null
@@ -0,0 +1,214 @@
+<?php 
+class executiveReportsView extends baseReportView {
+       
+       public function __construct() {}
+       
+       
+       public function showReportListLinks($params, $executiveReportsList) {
+
+               $pluralSuffix = (count($params->orgList)>1) ? 's' : ''; 
+               echo "Please click on the links below to view your report$pluralSuffix <br>";
+
+               //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 '<br>';
+                       if ($orgListObj->ou_type == 2) 
+                               echo '<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';      
+                       elseif ($orgListObj->ou_type == 3)
+                               echo '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
+                       echo $orgListObj->shortname,'&nbsp;';
+                       echo '&nbsp;&nbsp;<a target="_blank" class="executiveReportTypeLink" href="'.QR_CREATE_EXECUTIVE_REPORT_PAGE.$orgListObj->id.'/'.$params->reportYear.'/'.$params->reportMonth.'/'.$params->reportList.'">HTML</a>';
+                       echo '&nbsp;<span class="executiveReportTypeLink">|</span>';
+                       echo '&nbsp;<a class="executiveReportTypeLink" href="'.QR_CREATE_EXECUTIVE_REPORT_PAGE.'excel/'.$orgListObj->id.'/'.$params->reportYear.'/'.$params->reportMonth.'/'.$params->reportList.'">Excel</a>';
+               }
+               
+               echo '<br><br>';
+       }
+       
+       
+       public function showExecutiveReportsMenu($orgList, $executiveReportsList) {
+
+               ?>
+               <form name="reportForm" id="reportForm" method="post" action="<?php echo QR_LIST_EXECUTIVE_REPORTS_PAGE; ?>">
+               <table cellpadding="0" cellspacing="0" border="0" width="100%">
+               <tr>
+               <td><tr><td colspan="3" style="background-color:#15513d; color:#fff; font-weight:bold; font-size:11pt">Create Your Executive Snapshot Report:</td>
+               </tr>
+               <tr>
+               <td>
+                                       
+               <div id="executiveReport">
+               <table cellpadding="0" cellspacing="0" border="0">
+               <tr>
+               <td width="562">
+                       <table class="executiveReportMenu executiveReportTopMenu" cellpadding="0" cellspacing="0" border="0" width="100%">
+                       <tr>
+                               <td class="width10"><b>1.&nbsp;</b></td>
+                               <td><b>Choose Location(s)</b></td>
+                               <td></td>
+                       </tr>
+                       <tr>
+                               <td></td>
+                               <td>    
+                               <?php echo $this->createOrgUnitSelect($orgList, 'OUList', false); ?>
+                               </td>
+                       </tr>
+                       </table>
+               </td>
+               <td class="width20"></td>
+               <td>
+                       <table class="executiveReportMenu executiveReportTopMenu" cellpadding="0" cellspacing="0" border="0" width="100%">
+                       <tr>
+                               <td class="width10"><b>2.&nbsp;</b></td>
+                               <td><b>Choose Month / Year</b><br><br>  
+
+                               <img class="imageBottom" src="images/icon_16_left_green.png" onclick="javascript:setExecutiveReportDate(-1);">
+
+                               <select name="reportMonth" id="reportMonth">
+                                       <option value="1">Jan</option>
+                                       <option value="2">Feb</option>
+                                       <option value="3">Mar</option>
+                                       <option value="4">Apr</option>
+                                       <option value="5">May</option>
+                                       <option value="6">June</option>
+                                       <option value="7">July</option>
+                                       <option value="8">Aug</option>
+                                       <option value="9">Sep</option>
+                                       <option value="10">Oct</option>
+                                       <option value="11">Nov</option>
+                                       <option value="12">Dec</option>
+                               </select>
+                               
+                               <select name="reportYear" id="reportYear" class="">
+                                       <?php 
+                                       $lastReportYear = (date('n') != 1) ? date('Y') : date('Y')-1;
+                                       for ($i=$lastReportYear; $i>=QR_EXECUTIVE_REPORTS_START_YEAR; $i--) {
+                                               echo '<option value="'.$i.'">'.$i.'</option>';
+                                       }
+                                       ?>
+                               </select>
+                               <img class="imageBottom" src="images/icon_16_right_green.png" onclick="javascript:setExecutiveReportDate(1);">                  
+                               </td>
+                       </tr>
+                       </table>
+               </td>
+               </tr>
+               <tr style="height:10px"><td colspan="3"></td></tr>
+               <tr>
+               <td colspan="3">
+                       <table class="executiveReportMenu" cellpadding="0" cellspacing="0" border="0" width="100%">
+                       <tr>
+                               <td class="width10"><b>3.&nbsp;</b></td>
+                               <td><b>Choose Report Data</b> &nbsp;<a id="selectAllReportsLink" class="selectAllLink" href="javascript:void(0);" onclick="toggleAllCheckboxes('executiveReportCheckbox');">Select All</a></td>
+                               <td class="width20"></td>
+                               <td></td>
+                               <td class="width20"></td>
+                               <td colspan="2" align="right">
+                                       <?php if (NULL != QR_EXECUTIVE_REPORTS_DESCRIPTION_URL) 
+                                               echo '<a id="executiveReportDefinitionsLink" target="_blank" href="', QR_EXECUTIVE_REPORTS_DESCRIPTION_URL, '">Report Data Definitions</a>'; ?>
+                               </td>
+                       </tr>
+                       <tr style="height:10px"><td colspan="7"></td></tr>
+                       <tr>
+                               <td></td>
+                               <td><table class="executiveReportSubMenu executiveReportSubMenuRow1" cellpadding="0" cellspacing="0" border="0">
+                       <tr>
+                               <td>
+                       
+                       <?php 
+                       $newColumn = false;
+                       $numColumns = 1;
+                       $currentCategory = NULL;
+                       $categoryList = array();
+                       
+                       foreach ($executiveReportsList as $er) {
+
+                               // strip special characters and spaces from category    
+                               $cleanCategory = preg_replace(array('/[^a-zA-Z0-9_.]/'), array(''), $er->category);
+                                                               
+                               if (NULL == $currentCategory) {         //first row, first column
+                                       echo '<b>',$er->category,'</b> &nbsp;<a id="',$cleanCategory,'SelectAllReportsLink" class="selectAllLink" href="javascript:void(0);" onclick="toggleAllCheckboxes(\'', $cleanCategory,'\');">Select All</a>';
+                                       echo '<br><input class="executiveReportCheckbox ', $cleanCategory, '" type="checkbox" name="', strtolower($er->id),'" id="', strtolower($er->id),'">', $er->name;
+                                       $categoryList[] = $cleanCategory;               
+                               }
+                               
+                               if ($er->category == $currentCategory)
+                                       echo '<br><input class="executiveReportCheckbox ', $cleanCategory, '" type="checkbox" name="', strtolower($er->id),'" id="', strtolower($er->id),'">', $er->name;
+                               else {  //new category
+                                       if ($er->category != $currentCategory && NULL != $currentCategory) {
+                                               echo '</td></tr></table></td>';                                         
+                                               $newColumn = true;
+                                               if (QR_EXECUTIVE_REPORTS_MENU_COLUMNS == $numColumns) {
+                                                       $numColumns = 1;
+                                                       echo '</tr><tr style="height:15px"><td colspan="7"></td></tr><tr>';
+                                               }
+                                               else
+                                                       $numColumns++;
+                                       }
+                                       
+                                       if ($newColumn) {
+                                               $newColumn = false;
+                                               echo '<td></td>';
+                                               echo '<td><table class="executiveReportSubMenu executiveReportSubMenuRow1" cellpadding="0" cellspacing="0" border="0">';
+                                               echo '<tr><td>';
+                                               echo '<b>',$er->category,'</b> &nbsp;<a id="',$cleanCategory,'SelectAllReportsLink" class="selectAllLink" href="javascript:void(0);" onclick="toggleAllCheckboxes(\'', $cleanCategory,'\');">Select All</a>';
+                                               echo '<br><input class="executiveReportCheckbox ', $cleanCategory, '" type="checkbox" name="', strtolower($er->id),'" id="', strtolower($er->id),'">', $er->name;
+                                               $categoryList[] = $cleanCategory;                       
+                                       }
+                               }
+                               $currentCategory = $er->category;
+                       }
+                       ?>
+                                       </td></tr>
+                                       </table>
+                               </td>
+                               <td></td>
+                               <td></td>
+                               <td></td>
+                       </tr>                   
+                       </table>                        
+               </td>
+               <td></td>
+               <td>
+               
+               </td>
+               </tr>
+               
+               <tr>
+               <td align="center" colspan="7">
+                       <br>
+                       <input id="runButton" type="submit" class="submitButton" value="Run Report"> &nbsp;
+               </td>
+               </tr>
+               
+               </table>
+               </div>
+               
+               </td>
+               </tr>
+               </table>
+               </form> 
+               <br>
+               
+               <script type="text/javascript">
+               setExecutiveReportDate(0);
+               document.getElementById("reportForm").onsubmit = function(){
+                       return submitExecutiveReport(); 
+               }
+
+               <?php 
+               //set javascript select all toggle array values
+               foreach ($categoryList as $cl) {
+                       echo '
+                               lastSelectAllState["',$cl,'"] = false;';
+               }
+               ?>
+               </script>
+<?php 
+       }
+       
+}
+?>
+
index 939a4e2..5b17bb6 100644 (file)
@@ -22,7 +22,7 @@ class homePageView {
                <td>&nbsp;</td>
                <td>
                        <a class="homePageLink" href="<?php echo QR_SITE_ROOT,QR_LIST_REPORTS_PAGE; ?>"><b>My Quick Reports</b></a><br>
-                       View reports you have already run, or that you have scheduled.
+                       View reports you have already run or that you have scheduled.
                </td>
        </tr>
        <tr>
index a3abc01..0bcb3c1 100644 (file)
@@ -16,7 +16,9 @@ class pageView {
                <title><?php echo QR_DEFAULT_TITLE_TAG_TEXT; ?></title>
        
                <link type="text/css" href="css/site_v1.6.css" rel="stylesheet">
-               <script type="text/JavaScript" src="lib/site_v1.4.js"></script> 
+               <?php if (QR_EXECUTIVE_REPORTS_ENABLED) echo '<link type="text/css" href="css/executiveReportMenu_v1.0.css" rel="stylesheet">'; ?>
+
+               <script type="text/JavaScript" src="lib/site_v1.5.js"></script> 
        </head>
        <body>
        <div id="page">
@@ -92,8 +94,9 @@ class pageView {
        <div id="footer">
        <br>  
        <?php 
-               echo '&#64; 2015-',date('Y');
-               echo 'Georgia Public Library Service, a Unit of the University System of Georgia.<br>', QR_PAGE_FOOTER_TEXT;
+               echo '&copy; 2015-' . date('Y');
+               echo ' Georgia Public Library Service, a Unit of the University System of Georgia.<br/>';
+        echo '1800 Century Place Suite 150, Atlanta, GA 30345-4304 | 404-235-7200', QR_PAGE_FOOTER_TEXT;
        ?>              
        <br><br>
        </div>