finish basic public dashboard

This commit is contained in:
msquare 2017-12-12 21:57:57 +01:00
parent d5631297dc
commit ff94df53d6
14 changed files with 379 additions and 75 deletions

View File

@ -1,70 +1,22 @@
<?php <?php
use Engelsystem\Database\Db;
/** /**
* Loads all data for the public dashboard * Loads all data for the public dashboard
*/ */
function public_dashboard_controller() function public_dashboard_controller()
{ {
$stats = []; $stats = [
'needed-3-hours' => stats_angels_needed_three_hours(),
'needed-night' => stats_angels_needed_for_nightshifts(),
'angels-working' => stats_currently_working(),
'hours-to-work' => stats_hours_to_work()
];
$now = time(); $free_shifts = Shifts_free(time(), time() + 12 * 60 * 60);
$in3hours = $now + 3 * 60 * 60;
$result = Db::selectOne("
SELECT SUM(
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
- (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
) as `count`
FROM `Shifts`
WHERE ((`end` > ? AND `end` < ?) OR (`start` > ? AND `start` < ?))", [
$now,
$in3hours,
$now,
$in3hours
]);
$stats['needed-3-hours'] = $result['count'];
$night_start = parse_date('Y-m-d H:i', date('Y-m-d', time() + 12 * 60 * 60) . ' 02:00');
$night_end = $night_start + 6 * 60 * 60;
$result = Db::selectOne("
SELECT SUM(
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
- (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
) as `count`
FROM `Shifts`
WHERE ((`end` > ? AND `end` < ?) OR (`start` > ? AND `start` < ?))", [
$night_start,
$night_end,
$night_start,
$night_end
]);
$stats['needed-night'] = $result['count'];
$result = Db::selectOne("
SELECT SUM(
(SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
) as `count`
FROM `Shifts`
WHERE (`end` >= ? AND `start` <= ?)", [
time(),
time()
]);
$stats['angels-working'] = $result['count'];
$result = Db::selectOne("
SELECT ROUND(SUM(
(SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
* (`Shifts`.`end` - `Shifts`.`start`)/3600
)) as `count`
FROM `Shifts`
WHERE `end` >= ?", [
time()
]);
$stats['hours-to-work'] = $result['count'];
return [ return [
'Engelsystem Public Dashboard', _('Engelsystem Public Dashboard'),
public_dashboard_view($stats) public_dashboard_view($stats, $free_shifts)
]; ];
} }
?> ?>

View File

@ -22,6 +22,7 @@ $includeFiles = [
__DIR__ . '/../includes/model/ShiftsFilter.php', __DIR__ . '/../includes/model/ShiftsFilter.php',
__DIR__ . '/../includes/model/ShiftSignupState.php', __DIR__ . '/../includes/model/ShiftSignupState.php',
__DIR__ . '/../includes/model/ShiftTypes_model.php', __DIR__ . '/../includes/model/ShiftTypes_model.php',
__DIR__ . '/../includes/model/Stats.php',
__DIR__ . '/../includes/model/UserAngelTypes_model.php', __DIR__ . '/../includes/model/UserAngelTypes_model.php',
__DIR__ . '/../includes/model/UserDriverLicenses_model.php', __DIR__ . '/../includes/model/UserDriverLicenses_model.php',
__DIR__ . '/../includes/model/UserGroups_model.php', __DIR__ . '/../includes/model/UserGroups_model.php',

View File

@ -26,6 +26,29 @@ function Shifts_by_angeltype($angeltype) {
', [$angeltype['id'], $angeltype['id']]); ', [$angeltype['id'], $angeltype['id']]);
} }
/**
* Returns every shift with needed angels in the given time range.
*/
function Shifts_free($start, $end)
{
$shifts = Db::select("
SELECT *
FROM `Shifts`
WHERE (`end` > ? AND `start` < ?)
AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
> (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
ORDER BY `start`
", [
$start,
$end
]);
$free_shifts = [];
foreach ($shifts as $shift) {
$free_shifts[] = Shift($shift['SID']);
}
return $free_shifts;
}
/** /**
* Returns all shifts with a PSID (from frab import) * Returns all shifts with a PSID (from frab import)
*/ */

94
includes/model/Stats.php Normal file
View File

@ -0,0 +1,94 @@
<?php
use Engelsystem\Database\Db;
/**
* Returns the number of angels currently working.
*/
function stats_currently_working()
{
$result = Db::selectOne("
SELECT SUM(
(SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
) as `count`
FROM `Shifts`
WHERE (`end` >= ? AND `start` <= ?)", [
time(),
time()
]);
if (empty($result['count'])) {
return '-';
}
return $result['count'];
}
/**
* Return the number of hours still to work.
*/
function stats_hours_to_work()
{
$result = Db::selectOne("
SELECT ROUND(SUM(
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
* (`Shifts`.`end` - `Shifts`.`start`)/3600
)) as `count`
FROM `Shifts`
WHERE `end` >= ?", [
time()
]);
if (empty($result['count'])) {
return '-';
}
return $result['count'];
}
/**
* Returns the number of needed angels in the next 3 hours
*/
function stats_angels_needed_three_hours()
{
$now = time();
$in3hours = $now + 3 * 60 * 60;
$result = Db::selectOne("
SELECT SUM(
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
- (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
) as `count`
FROM `Shifts`
WHERE ((`end` > ? AND `end` < ?) OR (`start` > ? AND `start` < ?))", [
$now,
$in3hours,
$now,
$in3hours
]);
if (empty($result['count'])) {
return '-';
}
return $result['count'];
}
/**
* Returns the number of needed angels for nightshifts (between 2 and 8)
*/
function stats_angels_needed_for_nightshifts()
{
$night_start = parse_date('Y-m-d H:i', date('Y-m-d', time() + 12 * 60 * 60) . ' 02:00');
$night_end = $night_start + 6 * 60 * 60;
$result = Db::selectOne("
SELECT SUM(
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
- (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
) as `count`
FROM `Shifts`
WHERE ((`end` > ? AND `end` < ?) OR (`start` > ? AND `start` < ?))", [
$night_start,
$night_end,
$night_start,
$night_end
]);
if (empty($result['count'])) {
return '-';
}
return $result['count'];
}
?>

View File

@ -1,5 +1,30 @@
<?php <?php
/**
* Render a stat for dashborad (big number with label).
* If no style given, style is danger if number > 0, and success if number == 0.
*
* @param string $label
* @param string $number
* @param string $style default, warning, danger or success. Optional.
*/
function stats($label, $number, $style = null)
{
if(empty($style)) {
if($number > 0) {
$style = 'danger';
} else {
$style = 'success';
}
}
return div('stats stats-' . $style, [
$label,
div('number', [
$number
])
]);
}
/** /**
* Renders tabs from the array. Array key is tab name, array value is tab content. * Renders tabs from the array. Array key is tab name, array value is tab content.
* *

View File

@ -3,29 +3,62 @@
/** /**
* Public dashboard (formerly known as angel news hub) * Public dashboard (formerly known as angel news hub)
*/ */
function public_dashboard_view($stats) function public_dashboard_view($stats, $free_shifts)
{ {
$shift_panels = [];
foreach ($free_shifts as $shift) {
$shift_panels[] = public_dashborad_shift_render($shift);
}
return page([ return page([
div('first', [ div('first container-fluid', [
div('col-xs-3 text-center', [ stats(_('Angels needed in the next 3 hrs'), $stats['needed-3-hours']),
_('Angels needed in the next 3 hrs'), stats(_('Angels needed for nightshifts'), $stats['needed-night']),
heading($stats['needed-3-hours'], 1) stats(_('Angels currently working'), $stats['angels-working'], 'default'),
]), stats(_('Hours to be worked'), $stats['hours-to-work'], 'default'),
div('col-xs-3 text-center', [
_('Angels needed for nightshifts'),
heading($stats['needed-night'], 1)
]),
div('col-xs-3 text-center', [
_('Angels currently working'),
heading($stats['angels-working'], 1)
]),
div('col-xs-3 text-center', [
_('Hours to be worked'),
heading($stats['hours-to-work'], 1)
]),
'<script>$(function(){setTimeout(function(){window.location.reload();}, 60000)})</script>' '<script>$(function(){setTimeout(function(){window.location.reload();}, 60000)})</script>'
]),
div('container-fluid first', [
heading(_('Needed angels:'), 1),
join($shift_panels)
]) ])
]); ]);
} }
/**
* Renders a single shift panel for a dashboard shift with needed angels
*/
function public_dashborad_shift_render($shift)
{
$style = 'default';
if (time() + 3 * 60 * 60 > $shift['start']) {
$style = 'warning';
}
if (time() > $shift['start']) {
$style = 'danger';
}
$panel_body = glyph('time') . date('H:i', $shift['start']) . ' - ' . date('H:i', $shift['end']);
$panel_body .= '<br>' . glyph('tasks') . ShiftType($shift['shifttype_id'])['name'];
if (! empty($shift['title'])) {
$panel_body .= ' (' . $shift['title'] . ')';
}
$panel_body .= '<br>' . glyph('map-marker') . Room($shift['RID'])['Name'];
foreach ($shift['NeedAngels'] as $needed_angels) {
$need = $needed_angels['count'] - $needed_angels['taken'];
if ($need > 0) {
$panel_body .= '<br>' . glyph('user') . $need . ' &times; ' . AngelType($needed_angels['TID'])['name'];
}
}
$panel_body = '<a href="' . shift_link($shift) . '">' . $panel_body . '</a>';
return div('panel panel-' . $style . ' col-xs-3', [
div('panel-body', [
heading($panel_body, 4)
])
]);
}
?> ?>

View File

@ -6736,6 +6736,28 @@ body {
line-height: 30px; line-height: 30px;
margin: 0px; margin: 0px;
} }
.stats {
position: relative;
float: left;
width: 25%;
min-height: 1px;
padding-left: 15px;
padding-right: 15px;
text-align: center;
}
.stats .number {
font-size: 80px;
font-weight: 200;
}
.stats-danger {
color: #d9534f;
}
.stats-warning {
color: #f0ad4e;
}
.stats-success {
color: #5cb85c;
}
.panel-primary .panel-heading a { .panel-primary .panel-heading a {
color: #ffffff; color: #ffffff;
} }

View File

@ -6759,6 +6759,28 @@ body {
line-height: 30px; line-height: 30px;
margin: 0px; margin: 0px;
} }
.stats {
position: relative;
float: left;
width: 25%;
min-height: 1px;
padding-left: 15px;
padding-right: 15px;
text-align: center;
}
.stats .number {
font-size: 80px;
font-weight: 200;
}
.stats-danger {
color: #d9534f;
}
.stats-warning {
color: #f0ad4e;
}
.stats-success {
color: #5cb85c;
}
.panel-primary .panel-heading a { .panel-primary .panel-heading a {
color: #ffffff; color: #ffffff;
} }

View File

@ -6736,6 +6736,28 @@ body {
line-height: 30px; line-height: 30px;
margin: 0px; margin: 0px;
} }
.stats {
position: relative;
float: left;
width: 25%;
min-height: 1px;
padding-left: 15px;
padding-right: 15px;
text-align: center;
}
.stats .number {
font-size: 80px;
font-weight: 200;
}
.stats-danger {
color: #7f528b;
}
.stats-warning {
color: #e3a14d;
}
.stats-success {
color: #7b9c41;
}
.panel-primary .panel-heading a { .panel-primary .panel-heading a {
color: #ffffff; color: #ffffff;
} }

View File

@ -6745,6 +6745,28 @@ body {
line-height: 30px; line-height: 30px;
margin: 0px; margin: 0px;
} }
.stats {
position: relative;
float: left;
width: 25%;
min-height: 1px;
padding-left: 15px;
padding-right: 15px;
text-align: center;
}
.stats .number {
font-size: 80px;
font-weight: 200;
}
.stats-danger {
color: #da1639;
}
.stats-warning {
color: #dad216;
}
.stats-success {
color: #39ab50;
}
.panel-primary .panel-heading a { .panel-primary .panel-heading a {
color: #ffffff; color: #ffffff;
} }

View File

@ -6759,6 +6759,28 @@ body {
line-height: 30px; line-height: 30px;
margin: 0px; margin: 0px;
} }
.stats {
position: relative;
float: left;
width: 25%;
min-height: 1px;
padding-left: 15px;
padding-right: 15px;
text-align: center;
}
.stats .number {
font-size: 80px;
font-weight: 200;
}
.stats-danger {
color: #ff263c;
}
.stats-warning {
color: #fff45f;
}
.stats-success {
color: #8dc123;
}
.panel-primary .panel-heading a { .panel-primary .panel-heading a {
color: #ffffff; color: #ffffff;
} }

View File

@ -6739,6 +6739,28 @@ body {
line-height: 30px; line-height: 30px;
margin: 0px; margin: 0px;
} }
.stats {
position: relative;
float: left;
width: 25%;
min-height: 1px;
padding-left: 15px;
padding-right: 15px;
text-align: center;
}
.stats .number {
font-size: 80px;
font-weight: 200;
}
.stats-danger {
color: #ff6600;
}
.stats-warning {
color: #ffff33;
}
.stats-success {
color: #99cc00;
}
.panel-primary .panel-heading a { .panel-primary .panel-heading a {
color: #ffffff; color: #ffffff;
} }

View File

@ -6762,6 +6762,28 @@ body {
line-height: 30px; line-height: 30px;
margin: 0px; margin: 0px;
} }
.stats {
position: relative;
float: left;
width: 25%;
min-height: 1px;
padding-left: 15px;
padding-right: 15px;
text-align: center;
}
.stats .number {
font-size: 80px;
font-weight: 200;
}
.stats-danger {
color: #ff6600;
}
.stats-warning {
color: #ffff33;
}
.stats-success {
color: #99cc00;
}
.panel-primary .panel-heading a { .panel-primary .panel-heading a {
color: #ffffff; color: #ffffff;
} }

View File

@ -21,6 +21,28 @@ body {
margin: 0px; margin: 0px;
} }
.stats {
.make-xs-column(3);
text-align: center;
.number {
font-size: 80px;
font-weight: 200;
}
}
.stats-danger {
color: @brand-danger;
}
.stats-warning {
color: @brand-warning;
}
.stats-success {
color: @brand-success;
}
.panel-primary .panel-heading a { .panel-primary .panel-heading a {
color: @panel-primary-text; color: @panel-primary-text;
} }