Добавил базовые датасеты и агрегации для конструктора отчетов
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Analytics\DataSets\Concerns;
|
||||
|
||||
use App\Services\Analytics\Dimension;
|
||||
use App\Services\Analytics\Measure;
|
||||
|
||||
/**
|
||||
* Общие измерения и показатели для пациентских источников (дежурный врач / медсестра).
|
||||
* Псевдоним пациентской таблицы передаётся параметром ($p), исходы считаются по той же
|
||||
* логике, что и ReportSourceRegistry::outcomeLabel (death_date / visit_result_id∈{4,14} / extract_date).
|
||||
*/
|
||||
trait PatientAggregates
|
||||
{
|
||||
private const URGENCY_OPTIONS = [1 => 'Планово', 2 => 'Экстренно'];
|
||||
|
||||
private function urgencyDimension(string $p): Dimension
|
||||
{
|
||||
return new Dimension(
|
||||
'urgency',
|
||||
'Срочность',
|
||||
'string',
|
||||
"{$p}.urgency_id",
|
||||
labels: fn () => self::URGENCY_OPTIONS,
|
||||
);
|
||||
}
|
||||
|
||||
private function outcomeDimension(string $p): Dimension
|
||||
{
|
||||
$expr = "CASE
|
||||
WHEN {$p}.death_date IS NOT NULL THEN 'Умер'
|
||||
WHEN COALESCE({$p}.visit_result_id, 0) IN (4, 14) THEN 'Переведён'
|
||||
WHEN {$p}.extract_date IS NOT NULL THEN 'Выписан'
|
||||
ELSE 'В отделении' END";
|
||||
|
||||
return new Dimension('outcome', 'Исход', 'string', $expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $p алиас таблицы пациентов
|
||||
* @param string|null $report алиас отчёта (report_duties/report_nurses) — если задан,
|
||||
* добавляется показатель «Поступило» (по дате поступления в период смены)
|
||||
* @return Measure[]
|
||||
*/
|
||||
private function patientMeasures(string $p, ?string $report = null): array
|
||||
{
|
||||
return [
|
||||
new Measure('patients_count', 'Количество пациентов', 'count', 'COUNT(*)'),
|
||||
...($report !== null ? [$this->admittedMeasure($p, $report)] : []),
|
||||
...$this->outcomeMeasures($p),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* «Поступило» — пациенты, чья дата поступления попала в период смены отчёта.
|
||||
* Требует алиас отчёта ($report), т.к. границы периода лежат в report_duties/report_nurses.
|
||||
*/
|
||||
private function admittedMeasure(string $p, string $report): Measure
|
||||
{
|
||||
return new Measure('admitted', 'Поступило', 'count',
|
||||
"SUM(CASE WHEN {$p}.recipient_date >= {$report}.period_start AND {$p}.recipient_date <= {$report}.period_end THEN 1 ELSE 0 END)");
|
||||
}
|
||||
|
||||
/** @return Measure[] */
|
||||
private function outcomeMeasures(string $p): array
|
||||
{
|
||||
return [
|
||||
new Measure('discharged', 'Выписано', 'count',
|
||||
"SUM(CASE WHEN {$p}.death_date IS NULL AND COALESCE({$p}.visit_result_id, 0) NOT IN (4, 14) AND {$p}.extract_date IS NOT NULL THEN 1 ELSE 0 END)"),
|
||||
new Measure('transferred', 'Переведено', 'count',
|
||||
"SUM(CASE WHEN {$p}.death_date IS NULL AND COALESCE({$p}.visit_result_id, 0) IN (4, 14) THEN 1 ELSE 0 END)"),
|
||||
new Measure('deceased', 'Умерло', 'count',
|
||||
"SUM(CASE WHEN {$p}.death_date IS NOT NULL THEN 1 ELSE 0 END)"),
|
||||
new Measure('in_department', 'В отделении', 'count',
|
||||
"SUM(CASE WHEN {$p}.death_date IS NULL AND COALESCE({$p}.visit_result_id, 0) NOT IN (4, 14) AND {$p}.extract_date IS NULL THEN 1 ELSE 0 END)"),
|
||||
];
|
||||
}
|
||||
}
|
||||
146
app/Services/Analytics/DataSets/DepartmentStatisticsDataSet.php
Normal file
146
app/Services/Analytics/DataSets/DepartmentStatisticsDataSet.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Analytics\DataSets;
|
||||
|
||||
use App\Services\Analytics\AnalyticsQuery;
|
||||
use App\Services\Analytics\AnalyticsResult;
|
||||
use App\Services\Analytics\Column;
|
||||
use App\Services\Analytics\Contracts\DataSet;
|
||||
use App\Services\StatisticsService;
|
||||
|
||||
/**
|
||||
* Фиксированный отчёт «Статистика по отделениям» — переиспользует StatisticsService,
|
||||
* поэтому цифры 1:1 совпадают со страницей /statistic (профили, под-итоги «ИТОГО»).
|
||||
* Колонки заданы жёстко, выбор группировок/показателей не требуется.
|
||||
*/
|
||||
class DepartmentStatisticsDataSet implements DataSet
|
||||
{
|
||||
public function key(): string
|
||||
{
|
||||
return 'department_statistics';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Статистика по отделениям';
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Сводная статистика по всем отделениям';
|
||||
}
|
||||
|
||||
public function category(): string
|
||||
{
|
||||
return 'Сводные';
|
||||
}
|
||||
|
||||
public function fixed(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function dimensions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function measures(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function filters(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @return Column[] */
|
||||
private function columns(): array
|
||||
{
|
||||
return [
|
||||
new Column('department', 'Отделение', 'dimension', 'string'),
|
||||
new Column('beds', 'Кол-во коек', 'measure', 'number', 'count'),
|
||||
new Column('rec_all', 'Поступило: всего', 'measure', 'number', 'count'),
|
||||
new Column('rec_plan', 'Поступило: план', 'measure', 'number', 'count'),
|
||||
new Column('rec_emergency', 'Поступило: экстр', 'measure', 'number', 'count'),
|
||||
new Column('rec_transferred', 'Поступило: перевод', 'measure', 'number', 'count'),
|
||||
new Column('outcome', 'Выбыло', 'measure', 'number', 'count'),
|
||||
new Column('consist', 'Состоит', 'measure', 'number', 'count'),
|
||||
new Column('avg_bed_days', 'Ср. койко-день', 'measure', 'number', null),
|
||||
new Column('preoperative_days', 'Пред. опер. койко-день', 'measure', 'number', null),
|
||||
new Column('percent_loaded', '% загруженности', 'measure', 'number', 'percent'),
|
||||
new Column('lethality', '% смертности', 'measure', 'number', 'percent'),
|
||||
new Column('surgery_emergency', 'Операции Э', 'measure', 'number', 'count'),
|
||||
new Column('surgery_plan', 'Операции П', 'measure', 'number', 'count'),
|
||||
new Column('deceased', 'Умерло', 'measure', 'number', 'count'),
|
||||
new Column('staff', 'Мед. персонал', 'measure', 'number', 'count'),
|
||||
];
|
||||
}
|
||||
|
||||
public function run(AnalyticsQuery $query): AnalyticsResult
|
||||
{
|
||||
$columns = $this->columns();
|
||||
|
||||
if ($query->user === null) {
|
||||
return new AnalyticsResult($columns, []);
|
||||
}
|
||||
|
||||
$payload = app(StatisticsService::class)->getStatisticsData(
|
||||
$query->user,
|
||||
$query->dateRange->startSql(),
|
||||
$query->dateRange->endSql(),
|
||||
$query->dateRange->isOneDay,
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
foreach ($payload['data'] ?? [] as $item) {
|
||||
$rows[] = ! empty($item['isGroupHeader'])
|
||||
? $this->groupHeaderRow($columns, $item['groupName'] ?? '')
|
||||
: $this->dataRow($item);
|
||||
}
|
||||
|
||||
return new AnalyticsResult($columns, $rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Column[] $columns
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function groupHeaderRow(array $columns, string $name): array
|
||||
{
|
||||
$row = [];
|
||||
foreach ($columns as $column) {
|
||||
$row[$column->key] = '';
|
||||
}
|
||||
$row['department'] = $name;
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $item
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function dataRow(array $item): array
|
||||
{
|
||||
return [
|
||||
'department' => $item['department'] ?? '',
|
||||
'beds' => $item['beds'] ?? 0,
|
||||
'rec_all' => $item['recipients']['all'] ?? 0,
|
||||
'rec_plan' => $item['recipients']['plan'] ?? 0,
|
||||
'rec_emergency' => $item['recipients']['emergency'] ?? 0,
|
||||
'rec_transferred' => $item['recipients']['transferred'] ?? 0,
|
||||
'outcome' => $item['outcome'] ?? 0,
|
||||
'consist' => $item['consist'] ?? 0,
|
||||
'avg_bed_days' => $item['averageBedDays'] ?? 0,
|
||||
'preoperative_days' => $item['preoperativeDays'] ?? 0,
|
||||
'percent_loaded' => $item['percentLoadedBeds'] ?? 0,
|
||||
'lethality' => $item['lethality'] ?? 0,
|
||||
'surgery_emergency' => $item['surgical']['emergency'] ?? 0,
|
||||
'surgery_plan' => $item['surgical']['plan'] ?? 0,
|
||||
'deceased' => $item['deceased'] ?? 0,
|
||||
'staff' => $item['countStaff'] ?? 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
83
app/Services/Analytics/DataSets/NurseJournalDataSet.php
Normal file
83
app/Services/Analytics/DataSets/NurseJournalDataSet.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Analytics\DataSets;
|
||||
|
||||
use App\Models\Department;
|
||||
use App\Services\Analytics\AbstractDataSet;
|
||||
use App\Services\Analytics\AnalyticsQuery;
|
||||
use App\Services\Analytics\DataSets\Concerns\PatientAggregates;
|
||||
use App\Services\Analytics\Dimension;
|
||||
use App\Services\Analytics\FilterDef;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class NurseJournalDataSet extends AbstractDataSet
|
||||
{
|
||||
use PatientAggregates;
|
||||
|
||||
public function key(): string
|
||||
{
|
||||
return 'nurse_journal';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Журнал медсестры';
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Пациенты из сданных журналов старшей медсестры';
|
||||
}
|
||||
|
||||
public function category(): string
|
||||
{
|
||||
return 'Журнал';
|
||||
}
|
||||
|
||||
protected function dateField(): string
|
||||
{
|
||||
return 'p.recipient_date';
|
||||
}
|
||||
|
||||
protected function baseQuery(AnalyticsQuery $query): Builder
|
||||
{
|
||||
return DB::table('report_nurse_patients as p')
|
||||
->join('report_nurses as rn', 'rn.id', '=', 'p.report_nurse_id')
|
||||
->where('rn.rf_department_id', $query->department->department_id)
|
||||
->where('rn.status_id', 2)
|
||||
->where('rn.period_start', '>=', $query->dateRange->startSql())
|
||||
->where('rn.period_end', '<=', $query->dateRange->endSql());
|
||||
}
|
||||
|
||||
public function dimensions(): array
|
||||
{
|
||||
return [
|
||||
$this->urgencyDimension('p'),
|
||||
$this->outcomeDimension('p'),
|
||||
new Dimension('recipient_date', 'Дата поступления', 'date', 'CAST(p.recipient_date AS date)'),
|
||||
new Dimension(
|
||||
'department',
|
||||
'Отделение',
|
||||
'string',
|
||||
'rn.rf_department_id',
|
||||
labels: fn (array $ids) => Department::whereIn('department_id', $ids)->get()
|
||||
->mapWithKeys(fn ($d) => [$d->department_id => $d->name_full ?? $d->name_short])
|
||||
->all(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
public function measures(): array
|
||||
{
|
||||
return $this->patientMeasures('p', 'rn');
|
||||
}
|
||||
|
||||
public function filters(): array
|
||||
{
|
||||
return [
|
||||
new FilterDef('nurse', 'Медсестра', 'rn.rf_lpudoctor_id', 'select', null, 'nurse_doctors'),
|
||||
new FilterDef('urgency', 'Срочность', 'p.urgency_id', 'select', [1 => 'Планово', 2 => 'Экстренно']),
|
||||
];
|
||||
}
|
||||
}
|
||||
103
app/Services/Analytics/DataSets/NurseShiftsDataSet.php
Normal file
103
app/Services/Analytics/DataSets/NurseShiftsDataSet.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Analytics\DataSets;
|
||||
|
||||
use App\Models\Department;
|
||||
use App\Models\MisLpuDoctor;
|
||||
use App\Services\Analytics\AbstractDataSet;
|
||||
use App\Services\Analytics\AnalyticsQuery;
|
||||
use App\Services\Analytics\DataSets\Concerns\PatientAggregates;
|
||||
use App\Services\Analytics\Dimension;
|
||||
use App\Services\Analytics\FilterDef;
|
||||
use App\Services\Analytics\Measure;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Смены медсестры — сданные журналы (ReportNurse) с показателями по пациентам.
|
||||
* Позволяет медсестре сформировать и выгрузить список своих смен.
|
||||
*/
|
||||
class NurseShiftsDataSet extends AbstractDataSet
|
||||
{
|
||||
use PatientAggregates;
|
||||
|
||||
public function key(): string
|
||||
{
|
||||
return 'nurse_shifts';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Смены медсестры';
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Сданные журналы медсестры с показателями по пациентам';
|
||||
}
|
||||
|
||||
public function category(): string
|
||||
{
|
||||
return 'Журнал';
|
||||
}
|
||||
|
||||
protected function dateField(): string
|
||||
{
|
||||
return 'rn.period_start';
|
||||
}
|
||||
|
||||
protected function baseQuery(AnalyticsQuery $query): Builder
|
||||
{
|
||||
return DB::table('report_nurses as rn')
|
||||
->leftJoin('report_nurse_patients as p', 'p.report_nurse_id', '=', 'rn.id')
|
||||
->where('rn.rf_department_id', $query->department->department_id)
|
||||
->where('rn.status_id', 2)
|
||||
->where('rn.period_start', '>=', $query->dateRange->startSql())
|
||||
->where('rn.period_end', '<=', $query->dateRange->endSql());
|
||||
}
|
||||
|
||||
public function dimensions(): array
|
||||
{
|
||||
return [
|
||||
new Dimension('shift_date', 'Дата смены', 'date', 'CAST(rn.period_start AS date)'),
|
||||
new Dimension(
|
||||
'nurse',
|
||||
'Медсестра',
|
||||
'string',
|
||||
'rn.rf_lpudoctor_id',
|
||||
labels: fn (array $ids) => MisLpuDoctor::whereIn('LPUDoctorID', $ids)->get()
|
||||
->mapWithKeys(fn ($d) => [$d->LPUDoctorID => trim("{$d->FAM_V} {$d->IM_V} {$d->OT_V}") ?: "Сотрудник #{$d->LPUDoctorID}"])
|
||||
->all(),
|
||||
),
|
||||
$this->urgencyDimension('p'),
|
||||
$this->outcomeDimension('p'),
|
||||
new Dimension(
|
||||
'department',
|
||||
'Отделение',
|
||||
'string',
|
||||
'rn.rf_department_id',
|
||||
labels: fn (array $ids) => Department::whereIn('department_id', $ids)->get()
|
||||
->mapWithKeys(fn ($d) => [$d->department_id => $d->name_full ?? $d->name_short])
|
||||
->all(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
public function measures(): array
|
||||
{
|
||||
return [
|
||||
new Measure('shifts_count', 'Количество смен', 'count', 'COUNT(DISTINCT rn.id)'),
|
||||
new Measure('patients_count', 'Пациентов', 'count', 'COUNT(p.id)'),
|
||||
$this->admittedMeasure('p', 'rn'),
|
||||
...$this->outcomeMeasures('p'),
|
||||
];
|
||||
}
|
||||
|
||||
public function filters(): array
|
||||
{
|
||||
return [
|
||||
new FilterDef('nurse', 'Медсестра', 'rn.rf_lpudoctor_id', 'select', null, 'nurse_doctors'),
|
||||
new FilterDef('urgency', 'Срочность', 'p.urgency_id', 'select', [1 => 'Планово', 2 => 'Экстренно']),
|
||||
];
|
||||
}
|
||||
}
|
||||
93
app/Services/Analytics/DataSets/PatientsDataSet.php
Normal file
93
app/Services/Analytics/DataSets/PatientsDataSet.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Analytics\DataSets;
|
||||
|
||||
use App\Models\Department;
|
||||
use App\Models\MisLpuDoctor;
|
||||
use App\Services\Analytics\AbstractDataSet;
|
||||
use App\Services\Analytics\AnalyticsQuery;
|
||||
use App\Services\Analytics\DataSets\Concerns\PatientAggregates;
|
||||
use App\Services\Analytics\Dimension;
|
||||
use App\Services\Analytics\FilterDef;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class PatientsDataSet extends AbstractDataSet
|
||||
{
|
||||
use PatientAggregates;
|
||||
|
||||
public function key(): string
|
||||
{
|
||||
return 'patients';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Пациенты';
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Пациенты дежурных смен: поступления, исходы, срочность';
|
||||
}
|
||||
|
||||
public function category(): string
|
||||
{
|
||||
return 'Пациенты';
|
||||
}
|
||||
|
||||
protected function dateField(): string
|
||||
{
|
||||
return 'p.recipient_date';
|
||||
}
|
||||
|
||||
protected function baseQuery(AnalyticsQuery $query): Builder
|
||||
{
|
||||
return DB::table('report_duty_patients as p')
|
||||
->join('report_duties as rd', 'rd.id', '=', 'p.report_duty_id')
|
||||
->where('rd.rf_department_id', $query->department->department_id)
|
||||
->where('rd.status_id', 2)
|
||||
->where('rd.period_start', '>=', $query->dateRange->startSql())
|
||||
->where('rd.period_end', '<=', $query->dateRange->endSql());
|
||||
}
|
||||
|
||||
public function dimensions(): array
|
||||
{
|
||||
return [
|
||||
$this->urgencyDimension('p'),
|
||||
$this->outcomeDimension('p'),
|
||||
new Dimension('recipient_date', 'Дата поступления', 'date', 'CAST(p.recipient_date AS date)'),
|
||||
new Dimension(
|
||||
'doctor',
|
||||
'Врач',
|
||||
'string',
|
||||
'rd.rf_lpudoctor_id',
|
||||
labels: fn (array $ids) => MisLpuDoctor::whereIn('LPUDoctorID', $ids)->get()
|
||||
->mapWithKeys(fn ($d) => [$d->LPUDoctorID => trim("{$d->FAM_V} {$d->IM_V} {$d->OT_V}") ?: "Врач #{$d->LPUDoctorID}"])
|
||||
->all(),
|
||||
),
|
||||
new Dimension(
|
||||
'department',
|
||||
'Отделение',
|
||||
'string',
|
||||
'rd.rf_department_id',
|
||||
labels: fn (array $ids) => Department::whereIn('department_id', $ids)->get()
|
||||
->mapWithKeys(fn ($d) => [$d->department_id => $d->name_full ?? $d->name_short])
|
||||
->all(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
public function measures(): array
|
||||
{
|
||||
return $this->patientMeasures('p', 'rd');
|
||||
}
|
||||
|
||||
public function filters(): array
|
||||
{
|
||||
return [
|
||||
new FilterDef('doctor', 'Врач', 'rd.rf_lpudoctor_id', 'select', null, 'doctors'),
|
||||
new FilterDef('urgency', 'Срочность', 'p.urgency_id', 'select', [1 => 'Планово', 2 => 'Экстренно']),
|
||||
];
|
||||
}
|
||||
}
|
||||
122
app/Services/Analytics/DataSets/ShiftsDataSet.php
Normal file
122
app/Services/Analytics/DataSets/ShiftsDataSet.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Analytics\DataSets;
|
||||
|
||||
use App\Domain\Reports\ValueObjects\MetrikaConfig;
|
||||
use App\Models\Department;
|
||||
use App\Models\MisLpuDoctor;
|
||||
use App\Services\Analytics\AbstractDataSet;
|
||||
use App\Services\Analytics\AnalyticsQuery;
|
||||
use App\Services\Analytics\Dimension;
|
||||
use App\Services\Analytics\FilterDef;
|
||||
use App\Services\Analytics\Measure;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ShiftsDataSet extends AbstractDataSet
|
||||
{
|
||||
private const STATUS_OPTIONS = [1 => 'Черновик', 2 => 'Сдан'];
|
||||
|
||||
public function key(): string
|
||||
{
|
||||
return 'shifts';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Смены / дежурства';
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Показатели сданных дежурных смен отделения';
|
||||
}
|
||||
|
||||
public function category(): string
|
||||
{
|
||||
return 'Дежурства';
|
||||
}
|
||||
|
||||
protected function dateField(): string
|
||||
{
|
||||
return 'rd.period_start';
|
||||
}
|
||||
|
||||
protected function baseQuery(AnalyticsQuery $query): Builder
|
||||
{
|
||||
return DB::table('report_duties as rd')
|
||||
->leftJoin('duty_report_metric_results as m', 'm.rf_report_id', '=', 'rd.id')
|
||||
->where('rd.rf_department_id', $query->department->department_id)
|
||||
->where('rd.status_id', 2)
|
||||
->where('rd.period_start', '>=', $query->dateRange->startSql())
|
||||
->where('rd.period_end', '<=', $query->dateRange->endSql());
|
||||
}
|
||||
|
||||
public function dimensions(): array
|
||||
{
|
||||
return [
|
||||
new Dimension('shift_date', 'Дата смены', 'date', 'CAST(rd.period_start AS date)'),
|
||||
new Dimension(
|
||||
'doctor',
|
||||
'Врач',
|
||||
'string',
|
||||
'rd.rf_lpudoctor_id',
|
||||
labels: fn (array $ids) => MisLpuDoctor::whereIn('LPUDoctorID', $ids)->get()
|
||||
->mapWithKeys(fn ($d) => [$d->LPUDoctorID => trim("{$d->FAM_V} {$d->IM_V} {$d->OT_V}") ?: "Врач #{$d->LPUDoctorID}"])
|
||||
->all(),
|
||||
),
|
||||
new Dimension(
|
||||
'department',
|
||||
'Отделение',
|
||||
'string',
|
||||
'rd.rf_department_id',
|
||||
labels: fn (array $ids) => Department::whereIn('department_id', $ids)->get()
|
||||
->mapWithKeys(fn ($d) => [$d->department_id => $d->name_full ?? $d->name_short])
|
||||
->all(),
|
||||
),
|
||||
new Dimension(
|
||||
'status',
|
||||
'Статус',
|
||||
'string',
|
||||
'rd.status_id',
|
||||
labels: fn () => self::STATUS_OPTIONS,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
public function measures(): array
|
||||
{
|
||||
return [
|
||||
new Measure('shifts_count', 'Количество смен', 'count', 'COUNT(DISTINCT rd.id)'),
|
||||
new Measure('beds', 'Коек', 'count', $this->sum(MetrikaConfig::BEDS)),
|
||||
new Measure('recipient_plan', 'Поступило плановых', 'count', $this->sum(MetrikaConfig::PLAN)),
|
||||
new Measure('recipient_emergency', 'Поступило экстренных', 'count', $this->sum(MetrikaConfig::EMERGENCY)),
|
||||
new Measure('discharged', 'Выписано', 'count', $this->sum(MetrikaConfig::DISCHARGED)),
|
||||
new Measure('transferred', 'Переведено', 'count', $this->sum(MetrikaConfig::TRANSFERRED)),
|
||||
new Measure('deceased', 'Умерло', 'count', $this->sum(MetrikaConfig::DECEASED)),
|
||||
new Measure('surgery_plan', 'Операции плановые', 'count', $this->sum(MetrikaConfig::PLAN_SURGERY)),
|
||||
new Measure('surgery_emergency', 'Операции экстренные', 'count', $this->sum(MetrikaConfig::EMERGENCY_SURGERY)),
|
||||
new Measure('occupancy_percent', 'Занятость, %', 'percent', $this->avg(MetrikaConfig::DEPARTMENT_LOADED)),
|
||||
new Measure('avg_bed_days', 'Ср. койко-день', null, $this->avg(MetrikaConfig::AVERAGE_BED_DAYS)),
|
||||
new Measure('lethality_percent', 'Летальность, %', 'percent', $this->avg(MetrikaConfig::LETHALITY)),
|
||||
new Measure('staff_count', 'Мед. персонал', 'count', $this->avg(MetrikaConfig::STAFF_COUNT)),
|
||||
];
|
||||
}
|
||||
|
||||
public function filters(): array
|
||||
{
|
||||
return [
|
||||
new FilterDef('doctor', 'Врач', 'rd.rf_lpudoctor_id', 'select', null, 'doctors'),
|
||||
];
|
||||
}
|
||||
|
||||
private function sum(int $metricId): string
|
||||
{
|
||||
return "SUM(CASE WHEN m.rf_metrika_item_id = {$metricId} THEN NULLIF(m.value, '')::numeric ELSE 0 END)";
|
||||
}
|
||||
|
||||
private function avg(int $metricId): string
|
||||
{
|
||||
return "AVG(CASE WHEN m.rf_metrika_item_id = {$metricId} THEN NULLIF(m.value, '')::numeric END)";
|
||||
}
|
||||
}
|
||||
73
app/Services/Analytics/DataSets/UnwantedEventsDataSet.php
Normal file
73
app/Services/Analytics/DataSets/UnwantedEventsDataSet.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Analytics\DataSets;
|
||||
|
||||
use App\Models\Department;
|
||||
use App\Services\Analytics\AbstractDataSet;
|
||||
use App\Services\Analytics\AnalyticsQuery;
|
||||
use App\Services\Analytics\Dimension;
|
||||
use App\Services\Analytics\Measure;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UnwantedEventsDataSet extends AbstractDataSet
|
||||
{
|
||||
public function key(): string
|
||||
{
|
||||
return 'unwanted_events';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Нежелательные события';
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Нежелательные события дежурных смен отделения';
|
||||
}
|
||||
|
||||
public function category(): string
|
||||
{
|
||||
return 'События';
|
||||
}
|
||||
|
||||
protected function dateField(): string
|
||||
{
|
||||
return 'e.created_at';
|
||||
}
|
||||
|
||||
protected function baseQuery(AnalyticsQuery $query): Builder
|
||||
{
|
||||
return DB::table('duty_unwanted_events as e')
|
||||
->join('report_duties as rd', 'rd.id', '=', 'e.report_duty_id')
|
||||
->where('rd.rf_department_id', $query->department->department_id)
|
||||
->where('rd.status_id', 2)
|
||||
->where('rd.period_start', '>=', $query->dateRange->startSql())
|
||||
->where('rd.period_end', '<=', $query->dateRange->endSql());
|
||||
}
|
||||
|
||||
public function dimensions(): array
|
||||
{
|
||||
return [
|
||||
new Dimension('event_title', 'Событие', 'string', 'e.title'),
|
||||
new Dimension('created_date', 'Дата', 'date', 'CAST(e.created_at AS date)'),
|
||||
new Dimension(
|
||||
'department',
|
||||
'Отделение',
|
||||
'string',
|
||||
'rd.rf_department_id',
|
||||
labels: fn (array $ids) => Department::whereIn('department_id', $ids)->get()
|
||||
->mapWithKeys(fn ($d) => [$d->department_id => $d->name_full ?? $d->name_short])
|
||||
->all(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
public function measures(): array
|
||||
{
|
||||
return [
|
||||
new Measure('events_count', 'Количество событий', 'count', 'COUNT(*)'),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user