courbes d'historique

This commit is contained in:
Tykayn 2025-06-19 11:01:44 +02:00 committed by tykayn
parent 06ced163e6
commit d9219db84f
4 changed files with 233 additions and 30 deletions

View file

@ -106,6 +106,17 @@ class Place
#[ORM\Column(nullable: true)] #[ORM\Column(nullable: true)]
private ?\DateTime $osm_data_date = null; private ?\DateTime $osm_data_date = null;
#[ORM\Column(nullable: true)]
private ?int $osm_version = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $osm_user = null;
#[ORM\Column(nullable: true)]
private ?int $osm_uid = null;
#[ORM\Column(nullable: true)]
private ?int $osm_changeset = null;
public function getPlaceTypeName(): ?string public function getPlaceTypeName(): ?string
{ {
@ -198,7 +209,7 @@ class Place
/** /**
* mettre à jour le lieu selon les tags osm * mettre à jour le lieu selon les tags osm
*/ */
public function update_place_from_overpass_data(array $overpass_data) { public function update_place_from_overpass_data(array $overpass_data) {
if ( ! isset($overpass_data['tags']) || $overpass_data['tags'] == null) { if ( ! isset($overpass_data['tags']) || $overpass_data['tags'] == null) {
return; return;
@ -216,7 +227,8 @@ class Place
'addr:street' => '', 'addr:street' => '',
'website' => '', 'website' => '',
'wheelchair' => '', 'wheelchair' => '',
'note' => '' 'note' => '',
'fixme' => '',
], $overpass_data['tags'] ); ], $overpass_data['tags'] );
@ -231,15 +243,37 @@ class Place
->setLon($orignal_overpass_data['lon']) ->setLon($orignal_overpass_data['lon'])
->setName(isset($overpass_data['name']) && $overpass_data['name'] != '' ? $overpass_data['name'] : null); ->setName(isset($overpass_data['name']) && $overpass_data['name'] != '' ? $overpass_data['name'] : null);
// Traiter le timestamp OSM si disponible
if (isset($orignal_overpass_data['timestamp']) && $orignal_overpass_data['timestamp']) {
try {
$osmDate = new \DateTime($orignal_overpass_data['timestamp']);
$this->setOsmDataDate($osmDate);
} catch (\Exception $e) {
// En cas d'erreur de parsing de la date, on ignore
}
}
// Traiter les autres métadonnées OSM
if (isset($orignal_overpass_data['version'])) {
$this->setOsmVersion($orignal_overpass_data['version']);
}
if (isset($orignal_overpass_data['user'])) {
$this->setOsmUser($orignal_overpass_data['user']);
}
if (isset($orignal_overpass_data['uid'])) {
$this->setOsmUid($orignal_overpass_data['uid']);
}
if (isset($orignal_overpass_data['changeset'])) {
$this->setOsmChangeset($orignal_overpass_data['changeset']);
}
$mapping = [ $mapping = [
['key' => 'postcode', 'setter' => 'setZipCode', 'source' => $overpass_data], ['key' => 'postcode', 'setter' => 'setZipCode', 'source' => $overpass_data],
['key' => 'email', 'setter' => 'setEmail', 'source' => $overpass_data], ['key' => 'email', 'setter' => 'setEmail', 'source' => $overpass_data],
['key' => 'opening_hours', 'setter' => 'setHasOpeningHours', 'source' => $overpass_data['tags'] ?? []], ['key' => 'opening_hours', 'setter' => 'setHasOpeningHours', 'source' => $overpass_data['tags'] ?? []],
['key' => 'note', 'setter' => 'setNote', 'source' => $overpass_data['tags'] ?? []],
['key' => 'addr:housenumber', 'setter' => 'setHasAddress', 'source' => $overpass_data['tags'] ?? []], ['key' => 'addr:housenumber', 'setter' => 'setHasAddress', 'source' => $overpass_data['tags'] ?? []],
['key' => 'website', 'setter' => 'setHasWebsite', 'source' => $overpass_data['tags'] ?? []], ['key' => 'website', 'setter' => 'setHasWebsite', 'source' => $overpass_data['tags'] ?? []],
['key' => 'wheelchair', 'setter' => 'setHasWheelchair', 'source' => $overpass_data['tags'] ?? []], ['key' => 'wheelchair', 'setter' => 'setHasWheelchair', 'source' => $overpass_data['tags'] ?? []],
['key' => 'note', 'setter' => 'setHasNote', 'source' => $overpass_data['tags'] ?? []],
['key' => 'siret', 'setter' => 'setSiret', 'source' => $overpass_data['tags'] ?? []], ['key' => 'siret', 'setter' => 'setSiret', 'source' => $overpass_data['tags'] ?? []],
['key' => 'addr:street', 'setter' => 'setStreet', 'source' => $overpass_data['tags'] ?? []], ['key' => 'addr:street', 'setter' => 'setStreet', 'source' => $overpass_data['tags'] ?? []],
['key' => 'addr:housenumber', 'setter' => 'setHousenumber', 'source' => $overpass_data['tags'] ?? []], ['key' => 'addr:housenumber', 'setter' => 'setHousenumber', 'source' => $overpass_data['tags'] ?? []],
@ -251,6 +285,26 @@ class Place
} }
} }
// Traiter les notes et fixme
$noteContent = '';
$hasNote = false;
if (isset($orignal_overpass_data['tags']['note']) && $orignal_overpass_data['tags']['note'] !== '') {
$noteContent .= $orignal_overpass_data['tags']['note'];
$hasNote = true;
}
if (isset($orignal_overpass_data['tags']['fixme']) && $orignal_overpass_data['tags']['fixme'] !== '') {
if ($noteContent !== '') {
$noteContent .= "\n\n";
}
$noteContent .= "FIXME: " . $orignal_overpass_data['tags']['fixme'];
$hasNote = true;
}
$this->setNoteContent($noteContent);
$this->setHasNote($hasNote);
$this $this
// ->setOsmId($overpass_data['id']) // ->setOsmId($overpass_data['id'])
// ->setOsmKind($overpass_data['type']) // ->setOsmKind($overpass_data['type'])
@ -263,8 +317,7 @@ class Place
->setHasOpeningHours($overpass_data['opening_hours'] ? true : false) ->setHasOpeningHours($overpass_data['opening_hours'] ? true : false)
->setHasAddress($overpass_data['addr:housenumber'] && $overpass_data['addr:street'] ? true : false) ->setHasAddress($overpass_data['addr:housenumber'] && $overpass_data['addr:street'] ? true : false)
->setHasWebsite($overpass_data['website'] ? true : false) ->setHasWebsite($overpass_data['website'] ? true : false)
->setHasWheelchair($overpass_data['wheelchair'] and $overpass_data['wheelchair'] != '' ? true : false) ->setHasWheelchair($overpass_data['wheelchair'] and $overpass_data['wheelchair'] != '' ? true : false);
->setHasNote($overpass_data['note'] and $overpass_data['note'] != '' ? true : false);
} }
public function __construct() public function __construct()
@ -643,4 +696,52 @@ class Place
return $this; return $this;
} }
public function getOsmVersion(): ?int
{
return $this->osm_version;
}
public function setOsmVersion(?int $osm_version): static
{
$this->osm_version = $osm_version;
return $this;
}
public function getOsmUser(): ?string
{
return $this->osm_user;
}
public function setOsmUser(?string $osm_user): static
{
$this->osm_user = $osm_user;
return $this;
}
public function getOsmUid(): ?int
{
return $this->osm_uid;
}
public function setOsmUid(?int $osm_uid): static
{
$this->osm_uid = $osm_uid;
return $this;
}
public function getOsmChangeset(): ?int
{
return $this->osm_changeset;
}
public function setOsmChangeset(?int $osm_changeset): static
{
$this->osm_changeset = $osm_changeset;
return $this;
}
} }

View file

@ -101,10 +101,10 @@ public function find_siret($tags) {
public function get_export_query($zone) { public function get_export_query($zone) {
return <<<QUERY return <<<QUERY
[out:csv(::id,::type,::lat,::lon,name,amenity,shop,office,healthcare,"contact:email",email,"contact:phone",phone,"contact:website",website,image,url,wikidata, opening_hours,"contact:housenumber","addr:housenumber","contact:street","addr:street",note,fixme,harassment_prevention,cuisine,brand,tourism,source,zip_code,"ref:FR:SIRET")]; [out:csv(::id,::type,::lat,::lon,::timestamp,::version,::user,::uid,::changeset,name,amenity,shop,office,healthcare,"contact:email",email,"contact:phone",phone,"contact:website",website,image,url,wikidata, opening_hours,"contact:housenumber","addr:housenumber","contact:street","addr:street",note,fixme,harassment_prevention,cuisine,brand,tourism,source,zip_code,"ref:FR:SIRET")];
{{geocodeArea:"{$zone}, France"}}->.searchArea; {{geocodeArea:"{$zone}, France"}}->.searchArea;
{$this->overpass_base_places} {$this->overpass_base_places}
out skel qt; out meta;
QUERY; QUERY;
} }
@ -112,7 +112,7 @@ QUERY;
return '[out:json][timeout:25]; return '[out:json][timeout:25];
area["ref:INSEE"="'.$zone.'"]->.searchArea; area["ref:INSEE"="'.$zone.'"]->.searchArea;
'.$this->overpass_base_places.' '.$this->overpass_base_places.'
out center tags;'; out meta;';
} }
private $more_tags = ['image', 'ref:FR:SIRET']; private $more_tags = ['image', 'ref:FR:SIRET'];
@ -180,7 +180,14 @@ out center tags;';
'name' => $element['tags']['name'] ?? '', 'name' => $element['tags']['name'] ?? '',
'lat' => $element['lat'] ?? null, 'lat' => $element['lat'] ?? null,
'lon' => $element['lon'] ?? null, 'lon' => $element['lon'] ?? null,
'tags' => $element['tags'] 'tags' => $element['tags'],
// Métadonnées OSM
'timestamp' => $element['timestamp'] ?? null,
'version' => $element['version'] ?? null,
'user' => $element['user'] ?? null,
'uid' => $element['uid'] ?? null,
'changeset' => $element['changeset'] ?? null,
'modified' => $element['timestamp'] ?? null
]; ];
} }
} }

View file

@ -11,33 +11,124 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const ctx = document.getElementById('completionHistoryChart').getContext('2d'); const ctx = document.getElementById('completionHistoryChart').getContext('2d');
// Préparer les données pour chaque aspect
const labels = [
{% for stat in statsHistory %}
'{{ stat.date|date('d/m/Y') }}'{% if not loop.last %},{% endif %}
{% endfor %}
];
const completionData = [
{% for stat in statsHistory %}
{{ stat.completionPercent }}{% if not loop.last %},{% endif %}
{% endfor %}
];
const openingHoursData = [
{% for stat in statsHistory %}
{% if stat.placesCount > 0 %}
{{ ((stat.openingHoursCount / stat.placesCount) * 100)|round(1) }}{% if not loop.last %},{% endif %}
{% else %}
0{% if not loop.last %},{% endif %}
{% endif %}
{% endfor %}
];
const addressData = [
{% for stat in statsHistory %}
{% if stat.placesCount > 0 %}
{{ ((stat.addressCount / stat.placesCount) * 100)|round(1) }}{% if not loop.last %},{% endif %}
{% else %}
0{% if not loop.last %},{% endif %}
{% endif %}
{% endfor %}
];
const websiteData = [
{% for stat in statsHistory %}
{% if stat.placesCount > 0 %}
{{ ((stat.websiteCount / stat.placesCount) * 100)|round(1) }}{% if not loop.last %},{% endif %}
{% else %}
0{% if not loop.last %},{% endif %}
{% endif %}
{% endfor %}
];
const siretData = [
{% for stat in statsHistory %}
{% if stat.placesCount > 0 %}
{{ ((stat.siretCount / stat.placesCount) * 100)|round(1) }}{% if not loop.last %},{% endif %}
{% else %}
0{% if not loop.last %},{% endif %}
{% endif %}
{% endfor %}
];
new Chart(ctx, { new Chart(ctx, {
type: 'line', type: 'line',
data: { data: {
labels: [ labels: labels,
{% for stat in statsHistory %} datasets: [
'{{ stat.date|date('d/m/Y') }}'{% if not loop.last %},{% endif %} {
{% endfor %} label: 'Taux de complétion global (%)',
], data: completionData,
datasets: [{ borderColor: 'rgb(75, 192, 192)',
label: 'Taux de complétion (%)', backgroundColor: 'rgba(75, 192, 192, 0.1)',
data: [ tension: 0.3,
{% for stat in statsHistory %} fill: false,
{{ stat.completionPercent }}{% if not loop.last %},{% endif %} borderWidth: 3
{% endfor %} },
], {
borderColor: 'rgb(75, 192, 192)', label: 'Horaires d\'ouverture (%)',
backgroundColor: 'rgba(75, 192, 192, 0.2)', data: openingHoursData,
tension: 0.3, borderColor: 'rgb(255, 99, 132)',
fill: true backgroundColor: 'rgba(255, 99, 132, 0.1)',
}] tension: 0.3,
fill: false,
borderWidth: 2
},
{
label: 'Adresses (%)',
data: addressData,
borderColor: 'rgb(54, 162, 235)',
backgroundColor: 'rgba(54, 162, 235, 0.1)',
tension: 0.3,
fill: false,
borderWidth: 2
},
{
label: 'Sites web (%)',
data: websiteData,
borderColor: 'rgb(255, 205, 86)',
backgroundColor: 'rgba(255, 205, 86, 0.1)',
tension: 0.3,
fill: false,
borderWidth: 2
},
{
label: 'SIRET (%)',
data: siretData,
borderColor: 'rgb(153, 102, 255)',
backgroundColor: 'rgba(153, 102, 255, 0.1)',
tension: 0.3,
fill: false,
borderWidth: 2
}
]
}, },
options: { options: {
responsive: true, responsive: true,
plugins: { plugins: {
title: { title: {
display: true, display: true,
text: 'Évolution du taux de complétion au fil du temps' text: 'Évolution des taux de complétion par aspect au fil du temps'
},
legend: {
position: 'top',
labels: {
usePointStyle: true,
padding: 20
}
} }
}, },
scales: { scales: {
@ -46,7 +137,7 @@ document.addEventListener('DOMContentLoaded', function() {
max: 100, max: 100,
title: { title: {
display: true, display: true,
text: 'Complétion (%)' text: 'Pourcentage (%)'
} }
}, },
x: { x: {
@ -55,6 +146,10 @@ document.addEventListener('DOMContentLoaded', function() {
text: 'Date' text: 'Date'
} }
} }
},
interaction: {
intersect: false,
mode: 'index'
} }
} }
}); });

View file

@ -128,7 +128,7 @@
{% block javascripts %} {% block javascripts %}
{{ parent() }} {{ parent() }}
<script src='{{ asset('js/utils.js') }}'></script> {# <script src='{{ asset('js/utils.js') }}'></script> #}
<script type="module"> <script type="module">
import { adjustListGroupFontSize } from '{{ asset('js/utils.js') }}'; import { adjustListGroupFontSize } from '{{ asset('js/utils.js') }}';
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {