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)]);
|
||||
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.
|
||||
* If $includeTargets is true and a target is set, shows progress as "X ud af Y (Z%)".
|
||||
* 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
|
||||
$departments = [];
|
||||
@@ -172,88 +167,139 @@ class goals_progress_alert_renderer
|
||||
$outLines[] = (string)$hLine;
|
||||
}
|
||||
}
|
||||
foreach ($departments as $deptData) {
|
||||
$lines = [];
|
||||
$lines[] = $deptData['label'] . ':';
|
||||
// Calculate counts on the fly for each period using the criteria with department filter
|
||||
$deptId = $deptData['id'];
|
||||
$yesterdayCount = 0;
|
||||
$weekTotalCount = 0;
|
||||
$monthTotalCount = 0;
|
||||
// Change grouping: render by period, then list departments under each period
|
||||
$periods = [
|
||||
[
|
||||
'label' => 'Igår',
|
||||
'range' => self::getYesterdayRange(),
|
||||
],
|
||||
[
|
||||
'label' => 'Ugen total',
|
||||
'range' => self::getWeekToDateRange(),
|
||||
],
|
||||
[
|
||||
'label' => 'Måneden total',
|
||||
'range' => self::getMonthToDateRange(),
|
||||
],
|
||||
];
|
||||
|
||||
if ($deptId !== null) {
|
||||
// Base dept-filtered criteria
|
||||
$base = $criteria->withDepartmentFilter((int)$deptId);
|
||||
foreach ($periods as $idx => $period) {
|
||||
$label = (string)$period['label'];
|
||||
[$ps, $pe] = $period['range'];
|
||||
$range = self::clampToCriteriaWindow($criteria, $ps, $pe);
|
||||
|
||||
// Yesterday
|
||||
[$ys, $ye] = self::getYesterdayRange();
|
||||
$yr = self::clampToCriteriaWindow($criteria, $ys, $ye);
|
||||
if ($yr !== null) {
|
||||
[$ys2, $ye2] = $yr;
|
||||
// Period header
|
||||
$outLines[] = $label . ':';
|
||||
|
||||
foreach ($departments as $deptData) {
|
||||
$deptId = $deptData['id'];
|
||||
$count = 0;
|
||||
if ($deptId !== null && $range !== null) {
|
||||
[$rs, $re] = $range;
|
||||
$base = $criteria->withDepartmentFilter((int)$deptId);
|
||||
$c = clone $base;
|
||||
// goals_criteria timeframe expects \DateTime (mutable), convert from Immutable
|
||||
$c->start = \DateTime::createFromImmutable($ys2);
|
||||
$c->end = \DateTime::createFromImmutable($ye2);
|
||||
$yesterdayCount = (int)self::getProgress($c);
|
||||
// goals_criteria timeframe expects \DateTimeInterface
|
||||
$c->start = $rs;
|
||||
$c->end = $re;
|
||||
$count = self::getProgress($c);
|
||||
}
|
||||
|
||||
// Week-to-date (Mon..today)
|
||||
[$ws, $we] = self::getWeekToDateRange();
|
||||
$wr = self::clampToCriteriaWindow($criteria, $ws, $we);
|
||||
if ($wr !== null) {
|
||||
[$ws2, $we2] = $wr;
|
||||
$c = clone $base;
|
||||
$c->start = \DateTime::createFromImmutable($ws2);
|
||||
$c->end = \DateTime::createFromImmutable($we2);
|
||||
$weekTotalCount = (int)self::getProgress($c);
|
||||
if ($includeTargets && isset($criteria->target)) {
|
||||
// Set the target to (days since start of period) * (daily target)
|
||||
$target = self::getDailyTarget($criteria);
|
||||
$target = self::getTargetForDepartmentInTimeframe(
|
||||
$criteria,
|
||||
$rs,
|
||||
$re
|
||||
);
|
||||
$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)
|
||||
[$ms, $me] = self::getMonthToDateRange();
|
||||
$mr = self::clampToCriteriaWindow($criteria, $ms, $me);
|
||||
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);
|
||||
// Add a blank line between periods (but not after the last one)
|
||||
if ($idx < count($periods) - 1) {
|
||||
$outLines[] = '';
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
$now = new \DateTimeImmutable('now');
|
||||
|
||||
Reference in New Issue
Block a user