From 29fa41666d1f959cb3cdc91a833045d5c10d20ef Mon Sep 17 00:00:00 2001 From: Skylar Ittner Date: Sun, 19 Nov 2017 01:01:16 -0700 Subject: [PATCH] Add report generation --- composer.json | 4 +- composer.lock | 120 +++++++++++++++++++++++-- database.mwb | Bin 7863 -> 8508 bytes lang/en_us.php | 13 +++ lib/reports.php | 212 ++++++++++++++++++++++++++++++++++++++++++++ pages.php | 12 +++ pages/export.php | 48 ++++++++++ static/css/app.css | 4 + static/js/export.js | 31 +++++++ 9 files changed, 437 insertions(+), 7 deletions(-) create mode 100644 lib/reports.php create mode 100644 pages/export.php create mode 100644 static/js/export.js diff --git a/composer.json b/composer.json index 09111ba..afc0a60 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,9 @@ "type": "project", "require": { "catfan/medoo": "^1.2", - "guzzlehttp/guzzle": "^6.2" + "guzzlehttp/guzzle": "^6.2", + "league/csv": "^9.1", + "lapinator/ods-php-generator": "^0.0.3" }, "license": "OTHER", "authors": [ diff --git a/composer.lock b/composer.lock index 1896297..f55f26e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "1c8b61c5d506ae016285b99b20040cf0", + "hash": "809fda99e7cc89e7857f8a5a7bb0bc78", + "content-hash": "b8ba52c94c62ef0abbe09903ec2cdca8", "packages": [ { "name": "catfan/medoo", @@ -63,7 +64,7 @@ "sql", "sqlite" ], - "time": "2017-05-22T04:39:48+00:00" + "time": "2017-05-22 04:39:48" }, { "name": "guzzlehttp/guzzle", @@ -125,7 +126,7 @@ "rest", "web service" ], - "time": "2017-02-28T22:50:30+00:00" + "time": "2017-02-28 22:50:30" }, { "name": "guzzlehttp/promises", @@ -176,7 +177,7 @@ "keywords": [ "promise" ], - "time": "2016-12-20T10:07:11+00:00" + "time": "2016-12-20 10:07:11" }, { "name": "guzzlehttp/psr7", @@ -241,7 +242,114 @@ "uri", "url" ], - "time": "2017-03-20T17:10:46+00:00" + "time": "2017-03-20 17:10:46" + }, + { + "name": "lapinator/ods-php-generator", + "version": "v0.0.3", + "source": { + "type": "git", + "url": "https://github.com/Lapinator/odsPhpGenerator.git", + "reference": "575314c003c2ec3032813bedcc1d27032b7b7ab2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Lapinator/odsPhpGenerator/zipball/575314c003c2ec3032813bedcc1d27032b7b7ab2", + "reference": "575314c003c2ec3032813bedcc1d27032b7b7ab2", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Laurent VUIBERT", + "email": "lapinator@gmx.fr", + "homepage": "http://lapinator.net", + "role": "Developer" + } + ], + "description": "Open Document Spreadsheet (.ods) generator ", + "homepage": "https://odsphpgenerator.lapinator.net/", + "keywords": [ + "LibreOffice", + "ods" + ], + "time": "2016-04-14 21:51:27" + }, + { + "name": "league/csv", + "version": "9.1.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "bfa3aa8e755377cd6cc2242cfd074e48c7c300ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/bfa3aa8e755377cd6cc2242cfd074e48c7c300ba", + "reference": "bfa3aa8e755377cd6cc2242cfd074e48c7c300ba", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=7.0.10" + }, + "require-dev": { + "ext-curl": "*", + "friendsofphp/php-cs-fixer": "^2.0", + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Csv\\": "src" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "Csv data manipulation made easy in PHP", + "homepage": "http://csv.thephpleague.com", + "keywords": [ + "csv", + "export", + "filter", + "import", + "read", + "write" + ], + "time": "2017-10-20 08:03:26" }, { "name": "psr/http-message", @@ -291,7 +399,7 @@ "request", "response" ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2016-08-06 14:39:51" } ], "packages-dev": [], diff --git a/database.mwb b/database.mwb index 323e7fb5cc44049e38f3f0b6b33b52b32455cf26..2d9040ba1b24e3e31235cabbe6238334df60e148 100644 GIT binary patch literal 8508 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 diff --git a/lang/en_us.php b/lang/en_us.php index 6d29d23..47a583a 100644 --- a/lang/en_us.php +++ b/lang/en_us.php @@ -77,4 +77,17 @@ define("STRINGS", [ "add" => "Add", "choose a shift" => "Choose a shift", "shift assigned" => "Shift assigned.", + "report export" => "Reports/Export", + "report type" => "Report type", + "format" => "Format", + "generate report" => "Generate report", + "choose an option" => "Choose an option", + "csv file" => "CSV text file", + "ods file" => "ODS spreadsheet", + "html file" => "HTML web page", + "report filtered to" => "Report filtered to {name} ({username})", + "all users" => "All users", + "one user" => "One user", + "choose user" => "Type to choose user", + "filter" => "Filter" ]); \ No newline at end of file diff --git a/lib/reports.php b/lib/reports.php new file mode 100644 index 0000000..d70e2b8 --- /dev/null +++ b/lib/reports.php @@ -0,0 +1,212 @@ +has('report_access_codes', ["AND" => ['code' => $VARS['code'], 'expires[>]' => $date]])) { + dieifnotloggedin(); + } +} else { + dieifnotloggedin(); +} + +// Delete old DB entries +$database->delete('report_access_codes', ['expires[<=]' => $date]); + +if (LOADED) { + $user = null; + require_once __DIR__ . "/userinfo.php"; + require_once __DIR__ . "/login.php"; + if ($VARS['users'] != "all" && !is_empty($VARS['user']) && user_exists($VARS['user'])) { + $user = getUserByUsername($VARS['user']); + } + if (isset($VARS['type']) && isset($VARS['format'])) { + generateReport($VARS['type'], $VARS['format'], $user); + die(); + } else { + lang("invalid parameters"); + die(); + } +} + +function getShiftReport($user = null) { + global $database; + if ($user != null && array_key_exists('uid', $user)) { + $shifts = $database->select( + "shifts", [ + "[>]assigned_shifts" => ["shiftid" => "shiftid"] + ], [ + "shifts.shiftid", "shiftname", "start", "end", "days" + ], [ + "uid" => $user['uid'] + ] + ); + } else { + $shifts = $database->select( + "shifts", [ + "shiftid", "shiftname", "start", "end", "days" + ] + ); + } + $header = [lang("shiftid", false), lang("shift name", false), lang("start", false), lang("end", false), lang("workers", false), lang("sunday", false), lang("monday", false), lang("tuesday", false), lang("wednesday", false), lang("thursday", false), lang("friday", false), lang("saturday", false)]; + $out = [$header]; + for ($i = 0; $i < count($shifts); $i++) { + $daycodes = str_split($shifts[$i]['days'], 2); + $assigned = $database->count("assigned_shifts", ['shiftid' => $shifts[$i]["shiftid"]]); + $out[] = [ + $shifts[$i]["shiftid"], + $shifts[$i]["shiftname"], + date(TIME_FORMAT, strtotime($shifts[$i]['start'])), + date(TIME_FORMAT, strtotime($shifts[$i]['end'])), + $assigned . "", + (in_array("Su", $daycodes) == true ? "Y" : "N"), + (in_array("Mo", $daycodes) == true ? "Y" : "N"), + (in_array("Tu", $daycodes) == true ? "Y" : "N"), + (in_array("We", $daycodes) == true ? "Y" : "N"), + (in_array("Th", $daycodes) == true ? "Y" : "N"), + (in_array("Fr", $daycodes) == true ? "Y" : "N"), + (in_array("Sa", $daycodes) == true ? "Y" : "N") + ]; + } + return $out; +} + +function getReportData($type, $user = null) { + switch ($type) { + case "shifts": + return getShiftReport($user); + break; + default: + return [["error"]]; + } +} + +function dataToCSV($data, $name = "report", $user = null) { + $csv = Writer::createFromString(''); + $usernotice = ""; + $usertitle = ""; + if ($user != null && array_key_exists('username', $user) && array_key_exists('name', $user)) { + $usernotice = lang2("report filtered to", ["name" => $user['name'], "username" => $user['username']], false); + $usertitle = "_" . $user['username']; + $csv->insertOne([$usernotice]); + } + $csv->insertAll($data); + header('Content-type: text/csv'); + header('Content-Disposition: attachment; filename="' . $name . $usertitle . "_" . date("Y-m-d_Hi") . ".csv" . '"'); + echo $csv; + die(); +} + +function dataToODS($data, $name = "report", $user = null) { + $ods = new ods(); + $styleColumn = new odsStyleTableColumn(); + $styleColumn->setUseOptimalColumnWidth(true); + $headerstyle = new odsStyleTableCell(); + $headerstyle->setFontWeight("bold"); + $table = new odsTable($name); + + for ($i = 0; $i < count($data[0]); $i++) { + $table->addTableColumn(new odsTableColumn($styleColumn)); + } + + $usernotice = ""; + $usertitle = ""; + if ($user != null && array_key_exists('username', $user) && array_key_exists('name', $user)) { + $usernotice = lang2("report filtered to", ["name" => $user['name'], "username" => $user['username']], false); + $usertitle = "_" . $user['username']; + $row = new odsTableRow(); + $row->addCell(new odsTableCellString($usernotice)); + $table->addRow($row); + } + + $rowid = 0; + foreach ($data as $datarow) { + $row = new odsTableRow(); + foreach ($datarow as $cell) { + if ($rowid == 0) { + $row->addCell(new odsTableCellString($cell, $headerstyle)); + } else { + $row->addCell(new odsTableCellString($cell)); + } + } + $table->addRow($row); + $rowid++; + } + $ods->addTable($table); + $ods->downloadOdsFile($name . $usertitle . "_" . date("Y-m-d_Hi") . ".ods"); +} + +function dataToHTML($data, $name = "report", $user = null) { + global $SECURE_NONCE; + // HTML exporter doesn't like null values + for ($i = 0; $i < count($data); $i++) { + for ($j = 0; $j < count($data[$i]); $j++) { + if (is_null($data[$i][$j])) { + $data[$i][$j] = ''; + } + } + } + $usernotice = ""; + $usertitle = ""; + if ($user != null && array_key_exists('username', $user) && array_key_exists('name', $user)) { + $usernotice = "" . lang2("report filtered to", ["name" => $user['name'], "username" => $user['username']], false) . "
"; + $usertitle = "_" . $user['username']; + } + header('Content-type: text/html'); + $converter = new HTMLConverter(); + $out = "\n" + . "\n" + . "\n" + . "" . $name . $usertitle . "_" . date("Y-m-d_Hi") . "\n" + . << +STYLE + . $usernotice + . $converter->convert($data); + echo $out; +} + +function generateReport($type, $format, $user = null) { + $data = getReportData($type, $user); + switch ($format) { + case "ods": + dataToODS($data, $type, $user); + break; + case "html": + dataToHTML($data, $type, $user); + break; + case "csv": + default: + echo dataToCSV($data, $type, $user); + break; + } +} diff --git a/pages.php b/pages.php index 416643d..7e1dca4 100644 --- a/pages.php +++ b/pages.php @@ -51,6 +51,18 @@ define("PAGES", [ "static/js/addshift.js" ] ], + "export" => [ + "title" => "report export", + "navbar" => true, + "icon" => "download", + "styles" => [ + "static/css/easy-autocomplete.min.css" + ], + "scripts" => [ + "static/js/jquery.easy-autocomplete.min.js", + "static/js/export.js" + ] + ], "assignshift" => [ "title" => "assign shift", "navbar" => false, diff --git a/pages/export.php b/pages/export.php new file mode 100644 index 0000000..744bc78 --- /dev/null +++ b/pages/export.php @@ -0,0 +1,48 @@ + + +
+
+
+ + +
+
+ +
+ +
+
+ +
+
+
+ + +
+
+
+ insert('report_access_codes', ['code' => $code, 'expires' => date("Y-m-d H:i:s", strtotime("+5 minutes"))]); + ?> + + + +
\ No newline at end of file diff --git a/static/css/app.css b/static/css/app.css index 7049ef4..ba45b88 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -78,6 +78,10 @@ margin-left: 10px; } +#user-box { + display: inline-block; +} + /* ============================== THEMING diff --git a/static/js/export.js b/static/js/export.js new file mode 100644 index 0000000..07d3329 --- /dev/null +++ b/static/js/export.js @@ -0,0 +1,31 @@ +$("#genrptbtn").click(function () { + setTimeout(function () { + window.location.reload(); + }, 1000) +}); + +var options = { + url: "action.php", + ajaxSettings: { + dataType: "json", + method: "GET", + data: { + action: "autocomplete_user" + } + }, + preparePostData: function (data) { + data.q = $("#user-box").val(); + return data; + }, + getValue: function (element) { + return element.username; + }, + template: { + type: "custom", + method: function (value, item) { + return item.name + " " + item.username + ""; + } + } +}; + +$("#user-box").easyAutocomplete(options); \ No newline at end of file