312 lines
14 KiB
PHP
312 lines
14 KiB
PHP
<?php
|
||
/**
|
||
* Plugin Name: RR Auswertung
|
||
* Description: Diagramme und Statistiken für Rosins Restaurants
|
||
* Version: 1.0.0
|
||
* Author: Netz-Orth
|
||
* Text Domain: rr-auswertung
|
||
*/
|
||
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
define( 'RR_AUSWERTUNG_PATH', plugin_dir_path( __FILE__ ) );
|
||
define( 'RR_AUSWERTUNG_URL', plugin_dir_url( __FILE__ ) );
|
||
|
||
require_once RR_AUSWERTUNG_PATH . 'includes/class-rr-data.php';
|
||
require_once RR_AUSWERTUNG_PATH . 'includes/class-rr-charts.php';
|
||
|
||
final class RR_Auswertung {
|
||
|
||
private static $instance = null;
|
||
private $data;
|
||
private $charts;
|
||
|
||
public static function instance() {
|
||
if ( null === self::$instance ) {
|
||
self::$instance = new self();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
private function __construct() {
|
||
$this->data = new RR_Data();
|
||
$this->charts = new RR_Charts( $this->data );
|
||
|
||
add_action( 'wp_enqueue_scripts', [ $this, 'register_assets' ] );
|
||
$this->register_shortcodes();
|
||
}
|
||
|
||
public function register_assets() {
|
||
wp_register_script(
|
||
'chartjs',
|
||
RR_AUSWERTUNG_URL . 'assets/js/chart.min.js',
|
||
[],
|
||
'4.4.7',
|
||
false
|
||
);
|
||
wp_register_script(
|
||
'chartjs-datalabels',
|
||
RR_AUSWERTUNG_URL . 'assets/js/chartjs-datalabels.min.js',
|
||
[ 'chartjs' ],
|
||
'2.2.0',
|
||
false
|
||
);
|
||
wp_register_style(
|
||
'rr-charts',
|
||
RR_AUSWERTUNG_URL . 'assets/css/rr-charts.css',
|
||
[],
|
||
'1.0.0'
|
||
);
|
||
|
||
// Perfmatters / WP Rocket / Autoptimize: chart.min.js von Delay/Defer ausschließen
|
||
add_filter( 'perfmatters_delay_js_exclusions', function( $exclusions ) {
|
||
$exclusions[] = 'chart.min.js';
|
||
$exclusions[] = 'chartjs-datalabels.min.js';
|
||
return $exclusions;
|
||
} );
|
||
add_filter( 'perfmatters_defer_js_exclusions', function( $exclusions ) {
|
||
$exclusions[] = 'chart.min.js';
|
||
$exclusions[] = 'chartjs-datalabels.min.js';
|
||
return $exclusions;
|
||
} );
|
||
}
|
||
|
||
private function register_shortcodes() {
|
||
$shortcodes = [
|
||
'rr_status_chart' => 'render_status_chart',
|
||
'rr_staffel_status_chart' => 'render_staffel_status_chart',
|
||
'rr_testessen_verbesserung_chart' => 'render_testessen_verbesserung_chart',
|
||
'rr_region_chart' => 'render_region_chart',
|
||
'rr_region_status_chart' => 'render_region_status_chart',
|
||
'rr_staffel_ergebnis_chart' => 'render_staffel_ergebnis_chart',
|
||
];
|
||
|
||
foreach ( $shortcodes as $tag => $method ) {
|
||
add_shortcode( $tag, [ $this->charts, $method ] );
|
||
}
|
||
|
||
// Text-Shortcodes mit dynamischen Zahlen
|
||
add_shortcode( 'rr_text_status', [ $this, 'render_text_status' ] );
|
||
add_shortcode( 'rr_text_staffel', [ $this, 'render_text_staffel' ] );
|
||
add_shortcode( 'rr_text_testessen', [ $this, 'render_text_testessen' ] );
|
||
add_shortcode( 'rr_text_region', [ $this, 'render_text_region' ] );
|
||
add_shortcode( 'rr_text_region_status', [ $this, 'render_text_region_status' ] );
|
||
add_shortcode( 'rr_text_staffel_ergebnis', [ $this, 'render_text_staffel_ergebnis' ] );
|
||
add_shortcode( 'rr_text_intro', [ $this, 'render_text_intro' ] );
|
||
|
||
// Debug-Shortcode: zeigt Rohdaten + Chart.js Status
|
||
add_shortcode( 'rr_debug', [ $this, 'render_debug' ] );
|
||
}
|
||
|
||
public function render_text_intro( $atts ) {
|
||
$counts = $this->data->get_status_counts();
|
||
$total = array_sum( $counts );
|
||
$staffel_data = $this->data->get_staffel_status();
|
||
$staffeln = count( $staffel_data );
|
||
$rate = $total > 0 ? round( $counts['geöffnet'] / $total * 100 ) : 0;
|
||
|
||
return sprintf(
|
||
'<p><strong>„Rosins Restaurants"</strong> gehört zu den bekanntesten deutschen TV-Formaten, wenn es um die Gastronomie geht. Seit der ersten Staffel hat Sternekoch <strong>Frank Rosin</strong> in <strong>%d Staffeln</strong> insgesamt <strong>%d Restaurants</strong> in ganz Deutschland besucht und den Betreibern unter die Arme gegriffen. Doch wie nachhaltig ist seine Hilfe wirklich? Aktuell sind noch <strong>%d Restaurants (%d%%)</strong> geöffnet. Auf dieser Seite findest du alle Zahlen, Daten und Fakten zur Sendung – die Statistiken werden regelmäßig aktualisiert und spiegeln den aktuellen Stand der besuchten Betriebe wider.</p>',
|
||
$staffeln,
|
||
$total,
|
||
$counts['geöffnet'],
|
||
$rate
|
||
);
|
||
}
|
||
|
||
public function render_text_status( $atts ) {
|
||
$counts = $this->data->get_status_counts();
|
||
$total = array_sum( $counts );
|
||
$rate_open = $total > 0 ? round( $counts['geöffnet'] / $total * 100 ) : 0;
|
||
$rate_closed = $total > 0 ? round( $counts['geschlossen'] / $total * 100 ) : 0;
|
||
$rate_new = $total > 0 ? round( $counts['neuer Betreiber'] / $total * 100 ) : 0;
|
||
|
||
return sprintf(
|
||
'<p>Eine der häufigsten Fragen rund um die Sendung lautet: Wie viele der von Frank Rosin besuchten Restaurants existieren heute noch? Von insgesamt <strong>%d Restaurants</strong> sind aktuell noch <strong>%d geöffnet</strong> – das entspricht einer Überlebensrate von <strong>%d%%</strong>. Dem gegenüber stehen <strong>%d Betriebe (%d%%)</strong>, die dauerhaft schließen mussten. Weitere <strong>%d Restaurants (%d%%)</strong> werden heute unter neuen Betreibern weitergeführt – das ursprüngliche Konzept oder der Besitzer hat also gewechselt, der Standort ist aber weiterhin in Betrieb.</p>',
|
||
$total,
|
||
$counts['geöffnet'],
|
||
$rate_open,
|
||
$counts['geschlossen'],
|
||
$rate_closed,
|
||
$counts['neuer Betreiber'],
|
||
$rate_new
|
||
);
|
||
}
|
||
|
||
public function render_text_staffel( $atts ) {
|
||
$staffel_data = $this->data->get_staffel_status();
|
||
$best_staffel = '';
|
||
$best_rate = 0;
|
||
$worst_staffel = '';
|
||
$worst_rate = 100;
|
||
|
||
foreach ( $staffel_data as $s => $data ) {
|
||
$sum = array_sum( $data );
|
||
if ( $sum > 0 ) {
|
||
$rate = $data['geöffnet'] / $sum * 100;
|
||
if ( $rate > $best_rate ) {
|
||
$best_rate = $rate;
|
||
$best_staffel = $s;
|
||
}
|
||
if ( $rate < $worst_rate ) {
|
||
$worst_rate = $rate;
|
||
$worst_staffel = $s;
|
||
}
|
||
}
|
||
}
|
||
|
||
return sprintf(
|
||
'<p>Nicht jede Staffel von Rosins Restaurants war gleich erfolgreich. Über <strong>%d Staffeln</strong> hinweg zeigen sich deutliche Unterschiede bei der langfristigen Überlebensrate der besuchten Betriebe. Die erfolgreichste Staffel ist <strong>Staffel %s</strong> – hier sind noch <strong>%d%%</strong> der Restaurants geöffnet. Am schlechtesten schneidet <strong>Staffel %s</strong> ab, wo nur noch <strong>%d%%</strong> der Betriebe existieren. Das folgende Diagramm schlüsselt die Verteilung von geöffneten, geschlossenen und unter neuer Führung stehenden Restaurants pro Staffel auf.</p>',
|
||
count( $staffel_data ),
|
||
$best_staffel,
|
||
round( $best_rate ),
|
||
$worst_staffel,
|
||
round( $worst_rate )
|
||
);
|
||
}
|
||
|
||
public function render_text_testessen( $atts ) {
|
||
$avg_data = $this->data->get_staffel_ergebnis_avg();
|
||
$total_avg1 = 0;
|
||
$total_avg2 = 0;
|
||
$count = 0;
|
||
|
||
foreach ( $avg_data as $s => $v ) {
|
||
if ( $v['avg1'] > 0 && $v['avg2'] > 0 ) {
|
||
$total_avg1 += $v['avg1'];
|
||
$total_avg2 += $v['avg2'];
|
||
$count++;
|
||
}
|
||
}
|
||
|
||
$avg1 = $count > 0 ? round( $total_avg1 / $count, 1 ) : 0;
|
||
$avg2 = $count > 0 ? round( $total_avg2 / $count, 1 ) : 0;
|
||
$diff = round( $avg2 - $avg1, 1 );
|
||
$pct = $avg1 > 0 ? round( $diff / $avg1 * 100 ) : 0;
|
||
|
||
return sprintf(
|
||
'<p>Ein zentrales Element jeder Folge ist das Testessen: Ein unabhängiges Team bewertet die Qualität der Restaurants auf einer normalisierten Skala von <strong>1 bis 5</strong>. Das erste Testessen findet vor Frank Rosins Eingreifen statt, das zweite nach der Umgestaltung. Im Durchschnitt über alle Staffeln verbessern sich die Restaurants von <strong>%.1f</strong> auf <strong>%.1f Punkte</strong> – das ist eine Steigerung um <strong>%.1f Punkte (+%d%%)</strong>. Doch wie konstant ist diese Verbesserung über die Jahre? Das folgende Diagramm zeigt den Vorher-Nachher-Vergleich der durchschnittlichen Testessen-Ergebnisse, aufgeschlüsselt nach Staffel.</p>',
|
||
$avg1,
|
||
$avg2,
|
||
$diff,
|
||
$pct
|
||
);
|
||
}
|
||
|
||
public function render_text_region( $atts ) {
|
||
$regions = $this->data->get_region_counts();
|
||
$top_region = '';
|
||
$top_count = 0;
|
||
$second_region = '';
|
||
$second_count = 0;
|
||
|
||
foreach ( $regions as $name => $c ) {
|
||
if ( $c > $top_count ) {
|
||
$second_count = $top_count;
|
||
$second_region = $top_region;
|
||
$top_count = $c;
|
||
$top_region = $name;
|
||
} elseif ( $c > $second_count ) {
|
||
$second_count = $c;
|
||
$second_region = $name;
|
||
}
|
||
}
|
||
|
||
return sprintf(
|
||
'<p>Die von Frank Rosin besuchten Restaurants verteilen sich auf <strong>%d Bundesländer und Regionen</strong> – darunter auch vereinzelte Einsätze im Ausland. Die regionale Verteilung ist dabei keineswegs gleichmäßig: Die meisten Hilferufe kamen aus <strong>%s</strong> mit <strong>%d Restaurants</strong>, gefolgt von <strong>%s</strong> mit <strong>%d Betrieben</strong>. Diese Verteilung spiegelt unter anderem die Dichte der Gastronomie-Szene und die wirtschaftliche Situation in den jeweiligen Regionen wider. Das horizontale Balkendiagramm zeigt die genaue Aufschlüsselung nach Bundesland und Region.</p>',
|
||
count( $regions ),
|
||
$top_region,
|
||
$top_count,
|
||
$second_region,
|
||
$second_count
|
||
);
|
||
}
|
||
|
||
public function render_text_region_status( $atts ) {
|
||
$region_status = $this->data->get_region_status();
|
||
$best_region = '';
|
||
$best_rate = 0;
|
||
$worst_region = '';
|
||
$worst_rate = 100;
|
||
|
||
foreach ( $region_status as $name => $data ) {
|
||
$sum = array_sum( $data );
|
||
if ( $sum >= 3 ) {
|
||
$rate = $data['geöffnet'] / $sum * 100;
|
||
if ( $rate > $best_rate ) {
|
||
$best_rate = $rate;
|
||
$best_region = $name;
|
||
}
|
||
if ( $rate < $worst_rate ) {
|
||
$worst_rate = $rate;
|
||
$worst_region = $name;
|
||
}
|
||
}
|
||
}
|
||
|
||
return sprintf(
|
||
'<p>Gibt es regionale Unterschiede beim langfristigen Erfolg der Restaurants? Die Daten zeigen: Ja. Betrachtet man nur Bundesländer und Regionen mit mindestens drei besuchten Restaurants, hat <strong>%s</strong> mit <strong>%d%%</strong> die höchste Überlebensrate. Am anderen Ende steht <strong>%s</strong> mit nur <strong>%d%%</strong> noch geöffneten Betrieben. Diese Unterschiede können auf verschiedene Faktoren zurückzuführen sein – von der lokalen Wirtschaftslage über Mietpreise bis hin zur Wettbewerbssituation vor Ort.</p>',
|
||
$best_region,
|
||
round( $best_rate ),
|
||
$worst_region,
|
||
round( $worst_rate )
|
||
);
|
||
}
|
||
|
||
public function render_text_staffel_ergebnis( $atts ) {
|
||
$avg_data = $this->data->get_staffel_ergebnis_avg();
|
||
$keys = array_keys( $avg_data );
|
||
$first = reset( $keys );
|
||
$last = end( $keys );
|
||
|
||
$first_avg2 = $avg_data[ $first ]['avg2'] ?? 0;
|
||
$last_avg2 = $avg_data[ $last ]['avg2'] ?? 0;
|
||
$trend = $last_avg2 > $first_avg2 ? 'verbessert' : ( $last_avg2 < $first_avg2 ? 'verschlechtert' : 'kaum verändert' );
|
||
|
||
// Beste Staffel nach 2. Testessen finden
|
||
$best_s = '';
|
||
$best_avg2 = 0;
|
||
foreach ( $avg_data as $s => $v ) {
|
||
if ( $v['avg2'] > $best_avg2 ) {
|
||
$best_avg2 = $v['avg2'];
|
||
$best_s = $s;
|
||
}
|
||
}
|
||
|
||
return sprintf(
|
||
'<p>Wie haben sich die Testessen-Bewertungen im Laufe der Sendung entwickelt? Von Staffel %s bis Staffel %s hat sich das durchschnittliche Ergebnis nach Rosins Einsatz insgesamt <strong>%s</strong> – von <strong>%.1f</strong> auf <strong>%.1f Punkte</strong>. Die stärkste Verbesserung nach dem Coaching erreichten die Restaurants in <strong>Staffel %s</strong> mit einem Durchschnitt von <strong>%.1f Punkten</strong> beim zweiten Testessen. Die beiden Linien im Diagramm zeigen den Trend der normalisierten Ergebnisse vor und nach Frank Rosins Einsatz über alle Staffeln hinweg.</p>',
|
||
$first,
|
||
$last,
|
||
$trend,
|
||
$first_avg2,
|
||
$last_avg2,
|
||
$best_s,
|
||
$best_avg2
|
||
);
|
||
}
|
||
|
||
public function render_debug( $atts ) {
|
||
if ( ! current_user_can( 'manage_options' ) ) {
|
||
return '<p>Debug nur für Admins sichtbar.</p>';
|
||
}
|
||
|
||
// Cache leeren für frische Daten
|
||
$this->data->clear_cache();
|
||
$info = $this->data->get_debug_info();
|
||
|
||
$out = '<div style="background:#f0f0f0;padding:20px;font-family:monospace;font-size:13px;overflow:auto;max-height:800px;">';
|
||
$out .= '<h3>RR Auswertung Debug</h3>';
|
||
$out .= '<pre>' . esc_html( print_r( $info, true ) ) . '</pre>';
|
||
$out .= '<h4>Chart.js Test</h4>';
|
||
$out .= '<p id="rr-debug-chartjs">Prüfe Chart.js...</p>';
|
||
$out .= '<script>document.addEventListener("DOMContentLoaded",function(){var el=document.getElementById("rr-debug-chartjs");if(typeof Chart!=="undefined"){el.innerHTML="Chart.js geladen: v"+Chart.version;}else{el.innerHTML="FEHLER: Chart.js NICHT geladen!";el.style.color="red";}});</script>';
|
||
$out .= '</div>';
|
||
|
||
return $out;
|
||
}
|
||
}
|
||
|
||
add_action( 'plugins_loaded', [ 'RR_Auswertung', 'instance' ] );
|