middleware('auth'); $this->middleware(function ($request, $next) { if (!ZenovaReportAccess::canView()) { abort(403); } return $next($request); }); $this->_service = 'reports'; $this->_module = 'zenova_reports'; $this->_viewname = 'zenovareport'; $this->_title = 'Glipiq & Sitazit Dashboard'; $this->_base_url = '/zenovareport'; } public function list(Request $request) { return redirect('/zenovareport/dashboard'); } public function dashboard(Request $request) { $level = $this->level($request); $built = $this->build($level); return view('zenovareport.dashboard', [ 'title' => $this->_title, 'level' => $level, 'groups' => $built['groups'], 'body' => $built['body'], 'totals' => $built['totals'], 'error' => $built['error'], ]); } public function export(Request $request) { $level = $this->level($request); $built = $this->build($level); if ($built['error']) { return redirect('/zenovareport/dashboard?level=' . $level)->with('error', $built['error']); } return Excel::download( new ZenovaReportExport($built['groups'], $built['raw_body'], $built['raw_totals']), 'glipiq-sitazit-level-' . $level . '.xlsx' ); } private function level(Request $request): int { $level = (int) $request->input('level', 3); return ($level >= 1 && $level <= 5) ? $level : 3; } private function build(int $level): array { $response = LambdaRequest::signedrequest( 'get', 'reports', 'zenova_reports/glipiqsitazit_report', ['level' => (string) $level], 120 ); $rows = []; $error = null; if (!$response || ($response->status ?? '') !== 'success') { $error = $response->message ?? 'Unable to load the Glipiq & Sitazit report.'; } else { $rows = is_array($response->data ?? null) ? $response->data : []; } $groups = $this->groups($rows); $columns = []; foreach ($groups as $group) { foreach ($group['columns'] as $column) { $columns[] = $column; } } $body = []; $rawBody = []; $rawTotals = array_fill(0, count($columns), 0); foreach ($rows as $row) { $line = []; $rawLine = []; foreach ($columns as $index => $column) { $raw = $this->rawValue($row, $column); if ($column['type'] !== 'text') { $rawTotals[$index] += $raw; } $rawLine[] = $raw; $line[] = $this->formatValue($raw, $column['type']); } $body[] = $line; $rawBody[] = $rawLine; } $totals = []; $rawTotalLine = []; if (!empty($rows)) { $fsos = $this->sumKey($rows, 'Demography & Details', 'No. of FSOs'); $glipiqActive = $this->sumKey($rows, 'Demography & Details', 'Glipiq Active FSOs'); $sitazitActive = $this->sumKey($rows, 'Demography & Details', 'Sitazit Active FSOs'); foreach ($columns as $index => $column) { if ($column['type'] === 'text') { $label = !empty($column['is_total']) ? 'Total' : ''; $totals[] = $label; $rawTotalLine[] = $label; continue; } if ($column['key'] === 'Glipiq Active FSO %') { $percent = $fsos ? ($glipiqActive / $fsos) * 100 : 0; $totals[] = $this->formatValue($percent, 'percent'); $rawTotalLine[] = $percent; continue; } if ($column['key'] === 'Sitazit Active FSO %') { $percent = $fsos ? ($sitazitActive / $fsos) * 100 : 0; $totals[] = $this->formatValue($percent, 'percent'); $rawTotalLine[] = $percent; continue; } $totals[] = $this->formatValue($rawTotals[$index], $column['type']); $rawTotalLine[] = $rawTotals[$index]; } } return [ 'groups' => $groups, 'body' => $body, 'totals' => $totals, 'raw_body' => $rawBody, 'raw_totals' => $rawTotalLine, 'error' => $error, ]; } private function groups(array $rows): array { $nameKey = 'Name of Region'; foreach ($rows as $row) { $demo = (array) ($row->{'Demography & Details'} ?? []); foreach (array_keys($demo) as $key) { if (strpos($key, 'Name of ') === 0) { $nameKey = $key; break 2; } } } $demoColumns = $this->geoColumns($nameKey); $currentType = strtolower(preg_replace('/^Name of /', '', $nameKey)); if (!in_array($currentType, ['zone', 'region', 'area'], true)) { $nameColumn = $this->column($nameKey, 'Demography & Details', $nameKey, 'text'); $nameColumn['is_total'] = true; $demoColumns[] = $nameColumn; } foreach ([ 'No. of FSOs' => 'number', 'No. of DSMs' => 'number', 'Glipiq Active FSOs' => 'number', 'Glipiq Active FSO %' => 'percent', 'Sitazit Active FSOs' => 'number', 'Sitazit Active FSO %' => 'percent', ] as $key => $type) { $demoColumns[] = $this->column($key, 'Demography & Details', $key, $type); } $dayMetrics = ['GLIPIQ Total Rx', 'ABSI Camp Count', 'Patients Screened Count', 'Sitazit Total RX']; $periodMetrics = [ 'GLIPIQ Total Rx', 'GLIPIQ New Conversion', 'ABSI Camp Count', 'Patients Screened Count', 'GLIPIQ Total POB Units', 'Sitazit Total RX', 'Sitazit New Conversions', 'Sitazit Total POB Units', ]; $groups = [ ['label' => 'Demography & Details', 'tone' => 'demo', 'columns' => $demoColumns], ]; $first = isset($rows[0]) ? $rows[0] : null; foreach ([ 'day5' => 'Day 5 Status', 'day4' => 'Day 4 Status', 'day3' => 'Day 3 Status', 'day2' => 'Day 2 Status', ] as $section => $title) { $groups[] = [ 'label' => $this->periodLabel($title, $this->sectionField($first, $section, 'date')), 'tone' => 'day', 'columns' => $this->metricColumns($section, $dayMetrics), ]; } $groups[] = [ 'label' => $this->periodLabel('Day 1 / Yesterday', $this->sectionField($first, 'day1', 'date')), 'tone' => 'yesterday', 'columns' => $this->metricColumns('day1', $dayMetrics), ]; $groups[] = [ 'label' => $this->periodLabel('Today', $this->sectionField($first, 'today', 'date')), 'tone' => 'today', 'columns' => $this->metricColumns('today', $periodMetrics), ]; $groups[] = [ 'label' => $this->periodLabel('MTD', $this->sectionField($first, 'mtd', 'period')), 'tone' => 'mtd', 'columns' => $this->metricColumns('mtd', $periodMetrics), ]; $groups[] = [ 'label' => $this->periodLabel('YTD', $this->sectionField($first, 'ytd', 'period')), 'tone' => 'ytd', 'columns' => $this->metricColumns('ytd', $periodMetrics), ]; return $groups; } private function geoColumns($nameKey) { $currentType = preg_replace('/^Name of /', '', $nameKey); $columns = []; foreach (['Zone', 'Region', 'Area'] as $type) { if (strcasecmp($currentType, $type) === 0) { $column = $this->column($type, 'Demography & Details', $nameKey, 'text'); $column['is_total'] = true; $columns[] = $column; continue; } $columns[] = $this->column($type, 'parent_positions', $type, 'text'); } return $columns; } private function column(string $label, string $section, string $key, string $type): array { return [ 'label' => $label, 'section' => $section, 'key' => $key, 'type' => $type, ]; } private function metricColumns(string $section, array $metrics): array { $columns = []; foreach ($metrics as $metric) { $columns[] = $this->column($metric, $section, $metric, 'number'); } return $columns; } private function periodLabel(string $title, string $period): string { return $period !== '' ? $title . ' (' . $period . ')' : $title; } private function sectionField($row, $section, $field) { if (!is_object($row) || !isset($row->{$section}) || !is_object($row->{$section})) { return ''; } $value = $row->{$section}->{$field} ?? ''; return $value === null ? '' : (string) $value; } private function rawValue($row, array $column) { if ($column['section'] === 'parent_positions') { $parents = (array) ($row->parent_positions ?? []); foreach ($parents as $key => $parentValue) { if (strcasecmp((string) $key, 'Country') === 0) { continue; } if (strcasecmp((string) $key, $column['key']) === 0) { return ($parentValue === null || $parentValue === '') ? '' : (string) $parentValue; } } return ''; } $section = $row->{$column['section']} ?? null; $value = is_object($section) ? ($section->{$column['key']} ?? null) : null; if ($column['type'] === 'text') { return ($value === null || $value === '') ? '-' : (string) $value; } return is_numeric($value) ? (float) $value : 0; } private function sumKey(array $rows, string $section, string $key): float { $total = 0; foreach ($rows as $row) { $value = $row->{$section}->{$key} ?? 0; if (is_numeric($value)) { $total += (float) $value; } } return $total; } private function formatValue($value, $type) { if ($type === 'text') { return (string) $value; } $number = (float) $value; if ($type === 'percent') { return number_format($number, 2) . '%'; } if (floor($number) == $number) { return number_format($number, 0); } return number_format($number, 2); } }