Globinours/app/Views/statistics.php

389 lines
26 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
$headline = ['handled' => [t('statistics.handled'),'teal'],'entries' => [t('statistics.entries'),'green'],'exits' => [t('statistics.exits'),'red'],'adoptions' => [t('statistics.adoptions'),'blue'],'deaths' => [t('statistics.deaths'),'dark'],'births' => [t('statistics.births'),'purple'],'vaccinations' => [t('statistics.vaccinations'),'cyan'],'treatments' => [t('statistics.treatments'),'orange'],'medical_notes' => [t('statistics.medical_notes'),'pink']];
$comparisonLabels = ['handled' => t('statistics.handled'),'entries' => t('statistics.entries'),'exits' => t('statistics.exits'),'adoptions' => t('statistics.adoptions'),'deaths' => t('statistics.deaths'),'births' => t('statistics.births'),'vaccinations' => t('statistics.vaccinations'),'treatments' => t('statistics.treatments_started'),'medical_notes' => t('statistics.medical_notes'),'avg' => t('statistics.avg_adoption_days')];
$intakeTypeLabels = [];
foreach (['unknown','found','owner_surrender','transfer','born_in_care','return','seizure','other'] as $key) {
$intakeTypeLabels[$key] = t('statistics.intake.' . $key);
}
$intakeReasonLabels = [];
foreach (['unknown','stray_found','unwanted_litter','owner_health','owner_death','owner_care_home','allergy','housing','financial','behavior','abandonment','danger','transfer','birth','other'] as $key) {
$intakeReasonLabels[$key] = t('statistics.reason.' . $key);
}
$distributionLabels = ['Femelles' => t('statistics.value.females'),'Mâles' => t('statistics.value.males'),'Inconnu' => t('statistics.value.unknown'),'Non renseigné' => t('statistics.value.not_provided'),'Non renseignée' => t('statistics.value.not_provided_f'),'Moins dun an' => t('statistics.value.under_year'),'1 à 2 ans' => t('statistics.value.one_two_years'),'3 à 7 ans' => t('statistics.value.three_seven_years'),'8 ans et plus' => t('statistics.value.eight_plus'),'Moins dun mois' => t('statistics.value.under_month'),'1 à 3 mois' => t('statistics.value.one_three_months'),'3 à 6 mois' => t('statistics.value.three_six_months'),'6 à 12 mois' => t('statistics.value.six_twelve_months'),'Plus dun an' => t('statistics.value.over_year'),'Chats' => t('statistics.value.cats'),'Chiens' => t('statistics.value.dogs'),'Inconnue' => t('statistics.value.unknown_f'),'Refuge' => t('status.refuge'),'Famille daccueil' => t('status.fa'),'Accueil permanent' => t('statistics.value.permanent_foster'),'Chats libres suivis' => t('statistics.free_cats'),'Quarantaine' => t('status.quarantaine'),'Infirmerie' => t('status.soin'),'Réservé' => t('status.reserve'),'Site non renseigné' => t('statistics.value.unprovided_site')];
$groups = [
[t('statistics.intake_modes'),$distributions['intakeTypes'],$intakeTypeLabels,'green'],
[t('statistics.intake_reasons'),$distributions['intakeReasons'],$intakeReasonLabels,'orange'],
[t('statistics.colors'),$distributions['colors'],[],'blue'],
[t('statistics.breeds'),$distributions['breeds'],[],'purple'],
[t('statistics.species'),$distributions['species'],[],'cyan'],
[t('statistics.sex'),$distributions['sex'],[],'pink'],
[t('statistics.age_at_entry'),$distributions['ages'],[],'orange'],
[t('statistics.origins'),$distributions['origins'],[],'green','municipality'],
[t('statistics.adoption_delay'),$distributions['stays'],[],'azure'],
[t('statistics.death_causes'),$distributions['deathCauses'],array_combine(array_keys(DeathController::CAUSES), array_map(static fn ($key) => DeathController::causeLabel($key), array_keys(DeathController::CAUSES))),'dark'],
[t('statistics.current_distribution'),$distributions['currentStatus'],[],'teal'],
[t('statistics.free_cat_sites'),$distributions['freeCatSites'] ?? [],[],'green'],
[$allYears ? t('statistics.releases_year') : t('statistics.releases_site'),$distributions['freeCatReleases'] ?? [],[],'lime'],
];
?>
<style>
@media print {
form[action="/statistics"] {
display: none !important
}
.page-body {
padding-top: 0
}
}
</style>
<div class="card mb-4 overflow-hidden">
<div class="card-status-top bg-cyan"></div>
<div class="card-body d-flex flex-wrap align-items-end gap-3">
<div class="me-auto">
<div class="subheader"><?= h(t('statistics.memory')) ?></div>
<h2 class="mb-1">
<?= $municipality !== '' ? t('statistics.city_report', ['city' => $municipality]) : t('statistics.figures') ?>
</h2>
<div class="text-muted"><?= $municipality !== '' ? t('statistics.city_help') : t('statistics.help') ?></div>
</div>
<form method="get" action="/statistics" class="d-flex flex-wrap align-items-end gap-2">
<div><label class="form-label"><?= h(t('statistics.period')) ?></label><select class="form-select"
name="year">
<option value="all" <?= $allYears ? 'selected' : '' ?>><?= h(t('statistics.all_years')) ?></option>
<?php foreach (array_reverse($years) as $y): ?><option value="<?= $y ?>"
<?= !$allYears && $y === $year ? 'selected' : '' ?>><?= $y ?></option><?php endforeach; ?>
</select></div>
<div><label class="form-label"><?= h(t('statistics.origin_city')) ?></label><select class="form-select"
name="municipality">
<option value=""><?= h(t('statistics.all_cities')) ?></option>
<?php foreach (($municipalities ?? []) as $city => $total): ?><option value="<?= h($city) ?>"
<?= $municipality === $city ? 'selected' : '' ?>><?= h($city) ?> · <?= (int)$total ?></option>
<?php endforeach; ?>
</select></div><button
class="btn btn-cyan"><?= h(t('common.show')) ?></button><?php if ($municipality !== ''): ?><a
class="btn btn-outline-secondary"
href="/statistics?year=<?= h($allYears ? 'all' : (string)$year) ?>"><?= h(t('common.clear')) ?></a><button
class="btn btn-outline-primary" type="button" onclick="window.print()">🖨
<?= h(t('common.print')) ?></button><?php endif; ?>
</form>
</div>
</div>
<?php if ($municipality !== ''): ?><div class="alert alert-info d-flex flex-wrap align-items-center gap-2">
<strong><?= h($municipality) ?> ·
<?= h($allYears ? t('statistics.all_years') : (string)$year) ?></strong><span><?= h(t('statistics.city_filter_summary', ['count' => (int)$stats['handled']])) ?></span><span
class="ms-auto small"><?= h(t('statistics.filter_applied')) ?></span></div><?php endif; ?>
<?php if ($municipality === '' && !empty($municipalityCoverage)): ?><div class="alert alert-info mb-4">
<div class="d-flex flex-wrap align-items-center gap-2">
<strong><?= h(t('statistics.location_coverage')) ?></strong><span
class="badge bg-green-lt"><?= h(t('statistics.location_documented', ['count' => $municipalityCoverage['documented']])) ?></span><span
class="badge bg-azure-lt"><?= h(t('statistics.location_inferred', ['count' => $municipalityCoverage['inferred']])) ?></span><span
class="badge bg-purple-lt"><?= h(t('statistics.location_born_in_care', ['count' => $municipalityCoverage['born_in_care']])) ?></span><span
class="badge bg-secondary-lt"><?= h(t('statistics.location_unknown', ['count' => $municipalityCoverage['unknown']])) ?></span>
</div>
<div class="small mt-1"><?= h(t('statistics.location_coverage_help')) ?></div>
</div><?php endif; ?>
<?php if ($allYears && $undatedDeaths): ?><div class="alert alert-info">
<?= h(t('statistics.undated_deaths', ['total' => (int)$stats['deaths'],'count' => (int)$undatedDeaths])) ?></div>
<?php endif; ?>
<?php if ($allYears): ?><div class="alert alert-info"><strong><?= h(t('statistics.global_memory')) ?></strong>
<?= h(t('statistics.undated_summary', ['entries' => (int)$undatedHandled,'exits' => (int)$undatedExits,'births' => (int)$undatedBirths,'vaccinations' => (int)$stats['vaccinations'],'vaccinated' => (int)($scopeCounts['vaccinated_animals'] ?? 0),'notes' => (int)$stats['medical_notes'],'medical' => (int)($scopeCounts['medical_animals'] ?? 0)])) ?>
</div><?php endif; ?>
<div class="row row-cards mb-4"><?php foreach ($headline as $key => [$label,$color]): ?><div class="col-6 col-md-3">
<div class="card h-100">
<div class="card-status-top bg-<?= $color ?>"></div>
<div class="card-body">
<div class="text-muted small text-uppercase"><?= h($label) ?></div>
<div class="display-6 fw-bold"><?= (int)$stats[$key] ?></div>
<div class="small text-muted">
<?= $allYears ? t('statistics.since_start') : t('statistics.in_year', ['year' => (int)$year]) ?>
</div>
</div>
</div>
</div><?php endforeach; ?></div>
<div class="card mb-4 overflow-hidden">
<div class="card-status-top bg-orange"></div>
<div class="card-header">
<div>
<h3 class="card-title"><?= h(t('statistics.vet_costs')) ?></h3>
<div class="text-muted small"><?= h(t('statistics.vet_costs_help')) ?></div>
</div>
<div class="ms-auto text-end">
<div class="h2 mb-0"><?= number_format(($costs['total_cents'] ?? 0) / 100, 2, ',', ' ') ?> €</div>
<div class="text-muted small">
<?= h(t('statistics.animals_concerned', ['count' => (int)($costs['animals'] ?? 0)])) ?></div>
</div>
</div>
<div class="card-body">
<div class="row g-3 mb-3">
<?php foreach ([[t('statistics.cost_scope_animals'),(int)($costs['scope_animals'] ?? 0),null],[t('statistics.cost_with_expense'),(int)($costs['animals'] ?? 0),null],[t('statistics.cost_avg_all'),(int)($costs['average_all_cents'] ?? 0),'money'],[t('statistics.cost_avg_spending'),(int)($costs['average_expense_cents'] ?? 0),'money'],[t('statistics.cost_median'),(int)($costs['median_expense_cents'] ?? 0),'money']] as [$label,$value,$format]): ?>
<div class="col-6 col-lg">
<div class="border rounded p-3 h-100">
<div class="text-muted small"><?= h($label) ?></div>
<div class="h3 mb-0">
<?= $format === 'money' ? number_format($value / 100, 2, ',', ' ') . ' €' : $value ?></div>
</div>
</div><?php endforeach;?></div>
<div class="alert alert-info py-2 small"><?= h(t('statistics.cost_metric_help')) ?></div>
<div class="row g-4">
<div class="col-md-6">
<h4 class="mb-1"><?= h(t('statistics.by_structure')) ?></h4>
<div class="text-muted small mb-2"><?= h(t('statistics.structure_cost_help')) ?></div>
<?php foreach (($costs['by_clinic'] ?? []) as $r): $structureUrl = !empty($r['contact_id']) ? '/directory/view?' . http_build_query(['id' => (int)$r['contact_id'],'year' => $allYears ? 'all' : $year,'municipality' => $municipality]) : null; ?>
<div class="d-flex justify-content-between align-items-start gap-3 border-bottom py-2">
<span><?php if ($structureUrl): ?><a class="fw-semibold" href="<?= h($structureUrl) ?>"
title="<?= h(t('statistics.open_structure_cost', ['structure' => $r['label']])) ?>"><?= h($r['label']) ?></a><?php else: ?><?= h($r['label']) ?><?php endif; ?><span
class="d-block text-muted small"><?= h(t('statistics.animals_short', ['count' => (int)$r['animals']])) ?>
· <?= number_format(((int)$r['average_cents']) / 100, 2, ',', ' ') ?> € /
<?= h(mb_strtolower(t('statistics.animals'))) ?></span></span><strong
class="text-nowrap"><?= number_format($r['total_cents'] / 100, 2, ',', ' ') ?> €</strong></div><?php endforeach;
if (empty($costs['by_clinic'])):?><span class="text-muted"><?= h(t('statistics.no_expense')) ?></span><?php endif;?>
</div>
<div class="col-md-6">
<h4 class="mb-1"><?= h(t('statistics.by_category')) ?></h4>
<div class="text-muted small mb-2"><?= h(t('statistics.category_cost_help')) ?></div>
<?php foreach (($costs['by_category'] ?? []) as $r): ?><div
class="d-flex justify-content-between align-items-start gap-3 border-bottom py-2">
<span><?= h(PricingService::categoryLabel($r['label'])) ?><span
class="d-block text-muted small"><?= h(t('statistics.animals_short', ['count' => (int)$r['animals']])) ?>
· <?= number_format(((int)$r['average_cents']) / 100, 2, ',', ' ') ?> € /
<?= h(mb_strtolower(t('statistics.animals'))) ?></span></span><strong
class="text-nowrap"><?= number_format($r['total_cents'] / 100, 2, ',', ' ') ?> €</strong></div>
<?php endforeach;?>
</div>
</div>
</div>
</div>
<?php if ($municipality === ''): ?><div class="card mb-4 overflow-hidden">
<div class="card-status-top bg-green"></div>
<div class="card-header">
<div>
<h3 class="card-title"><?= h(t('statistics.cost_by_city')) ?></h3>
<div class="text-muted small"><?= h(t('statistics.cost_by_city_help')) ?></div>
</div>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table mb-0">
<thead>
<tr>
<th><?= h(t('statistics.municipality')) ?></th>
<th class="text-end"><?= h(t('statistics.cost_scope_animals')) ?></th>
<th class="text-end"><?= h(t('statistics.expense_animals')) ?></th>
<th class="text-end"><?= h(t('statistics.total_cost')) ?></th>
<th class="text-end"><?= h(t('statistics.average_all')) ?></th>
<th class="text-end"><?= h(t('statistics.average_with_expense')) ?></th>
<th class="text-end"><?= h(t('statistics.median')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach (($municipalityCosts ?? []) as $row): $cityUrl = '/statistics?' . http_build_query(['year' => $allYears ? 'all' : $year,'municipality' => $row['municipality']]); ?>
<tr>
<td><a class="fw-semibold" href="<?= h($cityUrl) ?>"
title="<?= h(t('statistics.cost_city_open', ['city' => $row['municipality']])) ?>"><?= h($row['municipality']) ?></a>
</td>
<td class="text-end"><?= (int)$row['animals'] ?></td>
<td class="text-end"><?= (int)$row['animals_with_expense'] ?></td>
<td class="text-end fw-bold"><?= number_format($row['total_cents'] / 100, 2, ',', ' ') ?> €</td>
<td class="text-end"><?= number_format($row['average_all_cents'] / 100, 2, ',', ' ') ?> €</td>
<td class="text-end"><?= number_format($row['average_expense_cents'] / 100, 2, ',', ' ') ?> €</td>
<td class="text-end"><?= number_format($row['median_expense_cents'] / 100, 2, ',', ' ') ?> €</td>
</tr><?php endforeach;?><?php if (empty($municipalityCosts)): ?><tr>
<td colspan="7" class="text-muted text-center py-4"><?= h(t('statistics.cost_no_city')) ?></td>
</tr><?php endif;?></tbody>
</table>
</div>
</div><?php endif; ?>
<div class="row row-cards mb-4">
<div class="col-lg-8"><?php if ($allYears): ?><div class="card h-100">
<div class="card-header">
<div>
<h3 class="card-title"><?= h(t('statistics.annual_activity')) ?></h3>
<div class="text-muted small"><?= h(t('statistics.annual_activity_help')) ?></div>
</div>
</div>
<div class="card-body">
<div style="height:280px"><canvas id="activityHistoryChart"
aria-label="<?= h(t('statistics.chart_label')) ?>"></canvas></div><?php if ($undatedHandled): ?>
<div class="text-muted small mt-3">
<?= h(t('statistics.undated_handled', ['count' => (int)$undatedHandled])) ?></div><?php endif; ?>
</div>
<div class="table-responsive border-top">
<table class="table table-vcenter mb-0">
<thead>
<tr>
<th><?= h(t('statistics.year')) ?></th>
<th class="text-end"><?= h(t('statistics.handled')) ?></th>
<th class="text-end"><?= h(t('statistics.entries')) ?></th>
<th class="text-end"><?= h(t('statistics.exits')) ?></th>
<th class="text-end"><?= h(t('statistics.adoptions')) ?></th>
<th class="text-end"><?= h(t('statistics.deaths')) ?></th>
<th class="text-end"><?= h(t('statistics.births')) ?></th>
</tr>
</thead>
<tbody><?php foreach (array_reverse($timeline, true) as $timelineYear => $annual): ?><tr>
<td class="fw-bold"><?= (int)$timelineYear ?></td>
<?php foreach (['handled','entries','exits','adoptions','deaths','births'] as $key): ?><td
class="text-end"><?= (int)$annual[$key] ?></td><?php endforeach; ?>
</tr><?php endforeach; ?><?php if ($undatedHandled): ?><tr class="text-muted">
<td class="fw-bold"><?= h(t('statistics.unknown_date')) ?></td>
<td class="text-end"><?= (int)$undatedHandled ?></td>
<td class="text-end" colspan="5">—</td>
</tr><?php endif; ?></tbody>
</table>
</div>
</div><?php else: ?><div class="card h-100">
<div class="card-header">
<div>
<h3 class="card-title"><?= h(t('grant.comparison', ['year' => $year - 1])) ?></h3>
<div class="text-muted small"><?= h(t('statistics.no_previous_percent')) ?></div>
</div>
</div>
<div class="table-responsive">
<table class="table table-vcenter mb-0">
<thead>
<tr>
<th><?= h(t('grant.indicator')) ?></th>
<th class="text-end"><?= $year - 1 ?></th>
<th class="text-end"><?= $year ?></th>
<th class="text-end"><?= h(t('grant.evolution')) ?></th>
</tr>
</thead>
<tbody><?php foreach ($comparisonLabels as $key => $label): $c = $comparison[$key]; ?><tr>
<td><?= h($label) ?></td>
<td class="text-end"><?= (int)round($c['previous']) ?></td>
<td class="text-end fw-bold"><?= (int)round($c['current']) ?></td>
<td class="text-end"><span
class="badge <?= $c['difference'] > 0 ? 'bg-blue-lt' : ($c['difference'] < 0 ? 'bg-orange-lt' : 'bg-secondary-lt') ?>"><?= $c['difference'] > 0 ? '+' : '' ?><?= (int)round($c['difference']) ?><?= $c['percent'] !== null ? ' · ' . ($c['percent'] > 0 ? '+' : '') . number_format($c['percent'], 1, ',', ' ') . ' %' : '' ?></span>
</td>
</tr><?php endforeach; ?></tbody>
</table>
</div>
</div><?php endif; ?></div>
<div class="col-lg-4">
<div class="card h-100">
<div class="card-header">
<div>
<h3 class="card-title"><?= h(t('statistics.current_snapshot')) ?></h3>
<div class="text-muted small"><?= h(t('statistics.current_snapshot_help')) ?></div>
</div>
</div>
<div class="card-body">
<div class="display-5 fw-bold mb-3"><?= (int)($health['total'] ?? 0) ?> <span
class="fs-3 fw-normal text-muted"><?= h(t('statistics.animals')) ?></span></div>
<?php foreach ([[t('statistics.free_cats'),$health['free_cats'] ?? 0,'lime'],[t('statistics.sterilized'),$health['sterilized'] ?? 0,'green'],[t('statistics.chipped'),$health['chipped'] ?? 0,'blue'],[t('statistics.fiv_positive'),$health['fiv_positive'] ?? 0,'orange'],[t('statistics.felv_positive'),$health['felv_positive'] ?? 0,'red']] as [$label,$total,$color]): $pct = ($health['total'] ?? 0) > 0 ? round($total / $health['total'] * 100) : 0; ?>
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<span><?= h($label) ?></span><strong><?= (int)$total ?> · <?= $pct ?> %</strong></div>
<div class="progress progress-sm">
<div class="progress-bar bg-<?= $color ?>" style="width:<?= $pct ?>%"></div>
</div>
</div><?php endforeach; ?><div class="alert alert-info py-2 mb-0 small">
<?= h(t('statistics.free_cat_help')) ?></div>
</div>
</div>
</div>
</div>
<div class="d-flex flex-wrap align-items-baseline gap-2 mb-3">
<h2 class="mb-0"><?= h(t('statistics.profiles')) ?></h2><span
class="text-muted"><?= h($allYears ? t('statistics.profiles_help_all') : t('statistics.profiles_help_year', ['year' => (int)$year])) ?></span>
</div>
<div class="row row-cards"><?php foreach ($groups as $group): [$title,$items,$labels,$color] = $group;
$linkType = $group[4] ?? null;
$maximum = $items ? max($items) : 0;
$sum = array_sum($items); ?><div class="col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header">
<div>
<h3 class="card-title"><?= h($title) ?></h3>
<div class="text-muted small">
<?= h(t('statistics.observations', ['count' => $sum])) ?><?= $linkType === 'municipality' && $municipality === '' ? ' · ' . t('statistics.click_city') : '' ?>
</div>
</div>
</div>
<div class="card-body"><?php if (!$items): ?><div class="text-muted">
<?= h(t('statistics.no_period_data')) ?></div><?php else: ?><div class="vstack gap-3">
<?php foreach ($items as $label => $total): $display = $labels[$label] ?? $distributionLabels[$label] ?? $label;
$width = $maximum > 0 ? max(4, round($total / $maximum * 100)) : 0;
$pct = $sum > 0 ? round($total / $sum * 100) : 0;
$cityUrl = '/statistics?' . http_build_query(['year' => $allYears ? 'all' : $year,'municipality' => $label]); ?><div>
<div class="d-flex justify-content-between gap-3 mb-1">
<?php if ($linkType === 'municipality' && $municipality === ''): ?><a
class="text-truncate fw-semibold" href="<?= h($cityUrl) ?>"
title="<?= h(t('statistics.show_city', ['city' => $display])) ?>"><?= h($display) ?></a><?php else: ?><span
class="text-truncate"
title="<?= h($display) ?>"><?= h($display) ?></span><?php endif; ?><strong
class="text-nowrap"><?= (int)$total ?> <span
class="text-muted fw-normal small"><?= $pct ?> %</span></strong></div>
<div class="progress progress-sm">
<div class="progress-bar bg-<?= $color ?>" style="width:<?= $width ?>%"></div>
</div>
</div><?php endforeach; ?></div><?php endif; ?></div>
</div>
</div><?php endforeach; ?></div>
<?php if ($allYears): ?><script>
document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('activityHistoryChart');
if (!canvas || typeof Chart === 'undefined') return;
new Chart(canvas, {
type: 'line',
data: {
labels: < ? = json_encode(array_map('strval', array_keys($timeline)),
JSON_UNESCAPED_UNICODE) ? > ,
datasets: [{
label: < ? = json_encode(t('statistics.handled'), JSON_UNESCAPED_UNICODE) ?
> ,
data: < ? = json_encode(array_values(array_map(static fn($s) => (int) $s[
'handled'], $timeline))) ? > ,
borderColor: '#0ca678',
backgroundColor: 'rgba(12,166,120,.12)',
tension: .3
}, {
label: < ? = json_encode(t('statistics.adoptions'),
JSON_UNESCAPED_UNICODE) ? > ,
data: < ? = json_encode(array_values(array_map(static fn($s) => (int) $s[
'adoptions'], $timeline))) ? > ,
borderColor: '#206bc4',
backgroundColor: 'rgba(32,107,196,.12)',
tension: .3
}, {
label: < ? = json_encode(t('statistics.deaths'), JSON_UNESCAPED_UNICODE) ? >
,
data: < ? = json_encode(array_values(array_map(static fn($s) => (int) $s[
'deaths'], $timeline))) ? > ,
borderColor: '#343a40',
backgroundColor: 'rgba(52,58,64,.10)',
tension: .3
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false
},
plugins: {
legend: {
position: 'bottom'
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
precision: 0
}
}
}
}
});
});
</script><?php endif; ?>