Refactor goals_progress_alert_renderer for enhanced department summary rendering
- Simplify multi-period department progress calculations by restructuring logic to render by period first. - Add support for daily target calculations and timeframe-based target adjustments in `goals_criteria`. - Introduce `countOperatingDaysInTimeframe` and related helper methods for improved alert scheduling and progress tracking.
This commit is contained in:
@@ -643,4 +643,15 @@ class goals_criteria implements goals_criteria_i
|
|||||||
$new_criteria->departments->set([(new \objects\departments_o())->select($department_id)]);
|
$new_criteria->departments->set([(new \objects\departments_o())->select($department_id)]);
|
||||||
return $new_criteria;
|
return $new_criteria;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAlertDaysForDepartment(int $deptId): array
|
||||||
|
{
|
||||||
|
$alert_days = [];
|
||||||
|
foreach ($this->progress_alert_weekdays as $weekday) {
|
||||||
|
if ($weekday instanceof goals_criteria_progress_alert_weekday) {
|
||||||
|
$alert_days[] = $weekday;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $alert_days;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -135,11 +135,6 @@ class goals_progress_alert_renderer
|
|||||||
* Renders a Danish multi-line progress summary for yesterday, week-to-date, and month-to-date.
|
* Renders a Danish multi-line progress summary for yesterday, week-to-date, and month-to-date.
|
||||||
* If $includeTargets is true and a target is set, shows progress as "X ud af Y (Z%)".
|
* If $includeTargets is true and a target is set, shows progress as "X ud af Y (Z%)".
|
||||||
* Otherwise, shows just the raw counts.
|
* Otherwise, shows just the raw counts.
|
||||||
* It should display like this:
|
|
||||||
* {$departmentName}:
|
|
||||||
* Igår: 5 ud af 10 (50%)
|
|
||||||
* Ugen total: 20 ud af 50 (40%)
|
|
||||||
* Måneden total: 75 ud af 100 (75%)
|
|
||||||
*/
|
*/
|
||||||
// Collect departments to summarize (id, label, highlight) using stable ID list
|
// Collect departments to summarize (id, label, highlight) using stable ID list
|
||||||
$departments = [];
|
$departments = [];
|
||||||
@@ -172,88 +167,139 @@ class goals_progress_alert_renderer
|
|||||||
$outLines[] = (string)$hLine;
|
$outLines[] = (string)$hLine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($departments as $deptData) {
|
// Change grouping: render by period, then list departments under each period
|
||||||
$lines = [];
|
$periods = [
|
||||||
$lines[] = $deptData['label'] . ':';
|
[
|
||||||
// Calculate counts on the fly for each period using the criteria with department filter
|
'label' => 'Igår',
|
||||||
$deptId = $deptData['id'];
|
'range' => self::getYesterdayRange(),
|
||||||
$yesterdayCount = 0;
|
],
|
||||||
$weekTotalCount = 0;
|
[
|
||||||
$monthTotalCount = 0;
|
'label' => 'Ugen total',
|
||||||
|
'range' => self::getWeekToDateRange(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'label' => 'Måneden total',
|
||||||
|
'range' => self::getMonthToDateRange(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
if ($deptId !== null) {
|
foreach ($periods as $idx => $period) {
|
||||||
// Base dept-filtered criteria
|
$label = (string)$period['label'];
|
||||||
$base = $criteria->withDepartmentFilter((int)$deptId);
|
[$ps, $pe] = $period['range'];
|
||||||
|
$range = self::clampToCriteriaWindow($criteria, $ps, $pe);
|
||||||
|
|
||||||
// Yesterday
|
// Period header
|
||||||
[$ys, $ye] = self::getYesterdayRange();
|
$outLines[] = $label . ':';
|
||||||
$yr = self::clampToCriteriaWindow($criteria, $ys, $ye);
|
|
||||||
if ($yr !== null) {
|
foreach ($departments as $deptData) {
|
||||||
[$ys2, $ye2] = $yr;
|
$deptId = $deptData['id'];
|
||||||
|
$count = 0;
|
||||||
|
if ($deptId !== null && $range !== null) {
|
||||||
|
[$rs, $re] = $range;
|
||||||
|
$base = $criteria->withDepartmentFilter((int)$deptId);
|
||||||
$c = clone $base;
|
$c = clone $base;
|
||||||
// goals_criteria timeframe expects \DateTime (mutable), convert from Immutable
|
// goals_criteria timeframe expects \DateTimeInterface
|
||||||
$c->start = \DateTime::createFromImmutable($ys2);
|
$c->start = $rs;
|
||||||
$c->end = \DateTime::createFromImmutable($ye2);
|
$c->end = $re;
|
||||||
$yesterdayCount = (int)self::getProgress($c);
|
$count = self::getProgress($c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Week-to-date (Mon..today)
|
if ($includeTargets && isset($criteria->target)) {
|
||||||
[$ws, $we] = self::getWeekToDateRange();
|
// Set the target to (days since start of period) * (daily target)
|
||||||
$wr = self::clampToCriteriaWindow($criteria, $ws, $we);
|
$target = self::getDailyTarget($criteria);
|
||||||
if ($wr !== null) {
|
$target = self::getTargetForDepartmentInTimeframe(
|
||||||
[$ws2, $we2] = $wr;
|
$criteria,
|
||||||
$c = clone $base;
|
$rs,
|
||||||
$c->start = \DateTime::createFromImmutable($ws2);
|
$re
|
||||||
$c->end = \DateTime::createFromImmutable($we2);
|
);
|
||||||
$weekTotalCount = (int)self::getProgress($c);
|
$percent = $target > 0 ? round(($count / max(1, $target)) * 100, 2) : 0.0;
|
||||||
|
// If the $department is set and matches the current department, make the related line stand out (*text here*)
|
||||||
|
$line = $deptData['highlight']
|
||||||
|
? "> *" . sprintf('%s: %d ud af %d (%.2f%%)', $deptData['label'], $count, $target, $percent) . "*"
|
||||||
|
: "- " . sprintf('%s: %d ud af %d (%.2f%%)', $deptData['label'], $count, $target, $percent);
|
||||||
|
} else {
|
||||||
|
// If the $department is set and matches the current department, make the related line stand out (*text here*)
|
||||||
|
$line = $deptData['highlight']
|
||||||
|
? "> *" .sprintf('%s: %d', $deptData['label'], $count) . "*"
|
||||||
|
: " - " .sprintf('%s: %d', $deptData['label'], $count);
|
||||||
}
|
}
|
||||||
|
$outLines[] = $line;
|
||||||
|
}
|
||||||
|
|
||||||
// Month-to-date (1st..today)
|
// Add a blank line between periods (but not after the last one)
|
||||||
[$ms, $me] = self::getMonthToDateRange();
|
if ($idx < count($periods) - 1) {
|
||||||
$mr = self::clampToCriteriaWindow($criteria, $ms, $me);
|
$outLines[] = '';
|
||||||
if ($mr !== null) {
|
|
||||||
[$ms2, $me2] = $mr;
|
|
||||||
$c = clone $base;
|
|
||||||
$c->start = \DateTime::createFromImmutable($ms2);
|
|
||||||
$c->end = \DateTime::createFromImmutable($me2);
|
|
||||||
$monthTotalCount = (int)self::getProgress($c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Yesterday
|
|
||||||
if ($includeTargets && isset($criteria->target)) {
|
|
||||||
$target = (int)$criteria->target;
|
|
||||||
$percentYest = $target > 0 ? round(($yesterdayCount / max(1, $target)) * 100, 2) : 0.0;
|
|
||||||
$lines[] = sprintf('Igår: %d ud af %d (%.2f%%)', $yesterdayCount, $target, $percentYest);
|
|
||||||
} else {
|
|
||||||
$lines[] = sprintf('Igår: %d', $yesterdayCount);
|
|
||||||
}
|
|
||||||
// Week total
|
|
||||||
if ($includeTargets && isset($criteria->target)) {
|
|
||||||
$target = (int)$criteria->target;
|
|
||||||
$percentWeek = $target > 0 ? round(($weekTotalCount / max(1, $target)) * 100, 2) : 0.0;
|
|
||||||
$lines[] = sprintf('Ugen total: %d ud af %d (%.2f%%)', $weekTotalCount, $target, $percentWeek);
|
|
||||||
} else {
|
|
||||||
$lines[] = sprintf('Ugen total: %d', $weekTotalCount);
|
|
||||||
}
|
|
||||||
// Month total
|
|
||||||
if ($includeTargets && isset($criteria->target)) {
|
|
||||||
$target = (int)$criteria->target;
|
|
||||||
$percentMonth = $target > 0 ? round(($monthTotalCount / max(1, $target)) * 100, 2) : 0.0;
|
|
||||||
$lines[] = sprintf('Måneden total: %d ud af %d (%.2f%%)', $monthTotalCount, $target, $percentMonth);
|
|
||||||
} else {
|
|
||||||
$lines[] = sprintf('Måneden total: %d', $monthTotalCount);
|
|
||||||
}
|
|
||||||
// Highlight if needed
|
|
||||||
if ($deptData['highlight']) {
|
|
||||||
$outLines[] = ">>> " . implode("\n", $lines);
|
|
||||||
} else {
|
|
||||||
$outLines[] = implode("\n", $lines);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return implode("\n\n", $outLines);
|
|
||||||
|
return implode("\n", $outLines);
|
||||||
|
}
|
||||||
|
// --- Date range helpers for summary periods ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the amount of days that alerts will be sent during the timeframe
|
||||||
|
* @param goals_criteria $criteria
|
||||||
|
* @param \DateTimeImmutable $rs
|
||||||
|
* @param \DateTimeImmutable $re
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function countOperatingDaysInTimeframe(
|
||||||
|
goals_criteria $criteria,
|
||||||
|
\DateTimeImmutable $rs,
|
||||||
|
\DateTimeImmutable $re
|
||||||
|
): int
|
||||||
|
{
|
||||||
|
$operatingWeekDays = $criteria->operating_days_of_week ?? [1, 2, 3, 4, 5]; // Default to Mon-Fri
|
||||||
|
$operatingDays = 0;
|
||||||
|
$current = $rs;
|
||||||
|
while ($current <= $re) {
|
||||||
|
$dayOfWeek = (int)$current->format('N'); // 1 (Mon) to 7 (Sun)
|
||||||
|
if (in_array($dayOfWeek, $operatingWeekDays, true)) {
|
||||||
|
$operatingDays++;
|
||||||
|
}
|
||||||
|
$current = $current->modify('+1 day');
|
||||||
|
}
|
||||||
|
return $operatingDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static function getDailyTarget(goals_criteria $criteria): float
|
||||||
|
{
|
||||||
|
$operatingDays = (new goals_progress_alert_renderer)->countOperatingDaysInTimeframe(
|
||||||
|
$criteria,
|
||||||
|
$criteria->start instanceof \DateTimeInterface ? \DateTimeImmutable::createFromMutable($criteria->start) : new \DateTimeImmutable('now'),
|
||||||
|
$criteria->end instanceof \DateTimeInterface ? \DateTimeImmutable::createFromMutable($criteria->end) : new \DateTimeImmutable('now')
|
||||||
|
);
|
||||||
|
if ($operatingDays <= 0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
return ((int)($criteria->target ?? 0)) / $operatingDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param goals_criteria $criteria
|
||||||
|
* @param \DateTimeImmutable $rs
|
||||||
|
* @param \DateTimeImmutable $re
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private static function getTargetForDepartmentInTimeframe(
|
||||||
|
goals_criteria $criteria,
|
||||||
|
\DateTimeImmutable $rs,
|
||||||
|
\DateTimeImmutable $re
|
||||||
|
): int
|
||||||
|
{
|
||||||
|
$dailyTarget = self::getDailyTarget($criteria);
|
||||||
|
if ($dailyTarget <= 0.0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$operatingDays = (new goals_progress_alert_renderer)->countOperatingDaysInTimeframe(
|
||||||
|
$criteria,
|
||||||
|
$rs,
|
||||||
|
$re
|
||||||
|
);
|
||||||
|
return (int)round($dailyTarget * $operatingDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Date range helpers for summary periods ---
|
|
||||||
private static function getYesterdayRange(): array
|
private static function getYesterdayRange(): array
|
||||||
{
|
{
|
||||||
$now = new \DateTimeImmutable('now');
|
$now = new \DateTimeImmutable('now');
|
||||||
|
|||||||
Reference in New Issue
Block a user