|
@@ -0,0 +1,265 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+class Schedule {
|
|
|
+ /** @var string[] $abbreviations */
|
|
|
+ private $abbreviations;
|
|
|
+
|
|
|
+ /** @var DateTime[] $displaydates */
|
|
|
+ private $displaydates;
|
|
|
+
|
|
|
+ /** @var string[] $stations */
|
|
|
+ private $stations;
|
|
|
+
|
|
|
+ /** @var string[] $stations */
|
|
|
+ private $optstations;
|
|
|
+
|
|
|
+ /** @var array[] $stations */
|
|
|
+ private $wantedstations;
|
|
|
+
|
|
|
+ /** @var array $improps */
|
|
|
+ private $improps;
|
|
|
+
|
|
|
+ /** @var GDIMAGE $this->image */
|
|
|
+ private $image;
|
|
|
+
|
|
|
+ /** @var boolean $darkmode */
|
|
|
+ private $darkmode;
|
|
|
+
|
|
|
+ /** @var boolean $debug */
|
|
|
+ private $debug;
|
|
|
+
|
|
|
+ public function __construct(array $displaydates, array $stations, array $optstations = [], $wantedstations = [], $darkmode = false) {
|
|
|
+ $this->abbreviations = [];
|
|
|
+ $this->training = $this->event = false;
|
|
|
+ $this->displaydates = $displaydates;
|
|
|
+ $this->stations = $stations;
|
|
|
+ $this->optstations = $optstations;
|
|
|
+ $this->wantedstations = $wantedstations;
|
|
|
+ $this->improps = [];
|
|
|
+ $this->darkmode = $darkmode;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function createImage() {
|
|
|
+ $rawData = $this->fetchData();
|
|
|
+
|
|
|
+ $relevantBookings = [];
|
|
|
+ foreach($this->stations as $station) {
|
|
|
+ $relevantBookings[$station] = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach($rawData as $item) {
|
|
|
+ if(in_array($item['station']['ident'], $this->stations)) {
|
|
|
+ $relevantBookings[$item['station']['ident']][] = [
|
|
|
+ 'starts_at' => new DateTime($item['starts_at']),
|
|
|
+ 'ends_at' => new DateTime($item['ends_at']),
|
|
|
+ 'lastname' => $item['controller']['lastname'],
|
|
|
+ 'firstname' => $item['controller']['firstname'],
|
|
|
+ 'training' => $item['training'],
|
|
|
+ 'event' => $item['event']
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ putenv('GDFONTPATH=' . realpath('.'));
|
|
|
+ $this->improps['font'] = "UbuntuMono-Regular";
|
|
|
+ // $this->improps['font'] = "EuroScope";
|
|
|
+ $this->improps['width'] = 100 * count($this->displaydates) + 100;
|
|
|
+ $this->improps['height'] = 25 * count($this->stations) + 600;
|
|
|
+
|
|
|
+ if(!$this->darkmode) {
|
|
|
+ $this->image = imagecreatetruecolor($this->improps['width'], $this->improps['height']);
|
|
|
+ $this->improps['bg'] = imagecolorallocate($this->image, 255, 255, 255);
|
|
|
+ $this->improps['black'] = imagecolorexact($this->image, 0, 0, 0);
|
|
|
+ $this->improps['open'] = imagecolorexact($this->image, 155, 155, 155);
|
|
|
+ $this->improps['wanted'] = imagecolorexact($this->image, 255, 0, 0);
|
|
|
+ $this->improps['event'] = imagecolorexact($this->image, 0, 255, 0);
|
|
|
+ $this->improps['training'] = imagecolorexact($this->image, 50, 100, 255);
|
|
|
+ $this->improps['eventtraining'] = imagecolorexact($this->image, 0, 255, 255);
|
|
|
+ } else {
|
|
|
+ $this->image = imagecreatetruecolor($this->improps['width'], $this->improps['height']);
|
|
|
+ $this->improps['bg'] = imagecolorallocate($this->image, 0, 0, 0);
|
|
|
+ $this->improps['black'] = imagecolorexact($this->image, 255, 255, 255);
|
|
|
+ $this->improps['open'] = imagecolorexact($this->image, 155, 155, 155);
|
|
|
+ $this->improps['wanted'] = imagecolorexact($this->image, 255, 0, 0);
|
|
|
+ $this->improps['event'] = imagecolorexact($this->image, 0, 255, 0);
|
|
|
+ $this->improps['training'] = imagecolorexact($this->image, 50, 100, 255);
|
|
|
+ $this->improps['eventtraining'] = imagecolorexact($this->image, 0, 255, 255);
|
|
|
+ }
|
|
|
+ imagefill($this->image, 0, 0, $this->improps['bg']);
|
|
|
+
|
|
|
+ $this->improps['lineheight'] = 20;
|
|
|
+ $sepheight = 25;
|
|
|
+
|
|
|
+ for($i = 0; $i < count($this->displaydates); $i++) {
|
|
|
+ imagefttext($this->image, 11, 0, ($i) * 100 + 100, 25, $this->improps['black'], $this->improps['font'], $this->displaydates[$i]->format("D d.m."));
|
|
|
+ }
|
|
|
+
|
|
|
+ $height = 40;
|
|
|
+ foreach($this->stations as $station) {
|
|
|
+ if($this->debug) echo "\n". $station . "\th$height";
|
|
|
+ imageline($this->image, 0, $height + 5, $this->improps['width'], $height + 5, $this->improps['open']);
|
|
|
+ if($station == '--') {
|
|
|
+ $height += $sepheight;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $height += $this->improps['lineheight'];
|
|
|
+
|
|
|
+ $lines = 0;
|
|
|
+ for($i = 0; $i < count($this->displaydates); $i++) {
|
|
|
+ $l = $this->addBookingData($station, $this->displaydates[$i], $relevantBookings[$station], $i * 100 + 100, $height);
|
|
|
+ $lines = $l > $lines ? $l : $lines;
|
|
|
+ if($this->debug) echo "\t" . $this->displaydates[$i]->format("m-d") . ": " . $lines;
|
|
|
+ }
|
|
|
+ if(!in_array($station, $this->optstations) && $lines < 1) $lines = 1;
|
|
|
+ if($this->debug) echo "\tl$lines";
|
|
|
+ if($lines > 0) {
|
|
|
+ imagefttext($this->image, 11, 0, 10, $height, $this->improps['black'], $this->improps['font'], $station);
|
|
|
+ imageline($this->image, 0, $height + 5, $this->improps['width'], $height + 5, $this->improps['open']);
|
|
|
+ }
|
|
|
+ $height += ($lines - 1) * $this->improps['lineheight'];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ for($i = 0; $i < count($this->displaydates); $i++) {
|
|
|
+ imageline($this->image, 100 * $i + 92, 0, 100 * $i + 92, $height + 5, $this->improps['open']);
|
|
|
+ }
|
|
|
+
|
|
|
+ $height += $this->improps['lineheight'] + 5;
|
|
|
+ imagefttext($this->image, 11, 0, 10, $height, $this->improps['wanted'], $this->improps['font'], 'Wanted');
|
|
|
+ imagefttext($this->image, 11, 0, 100, $height, $this->improps['training'], $this->improps['font'], 'Training');
|
|
|
+ imagefttext($this->image, 11, 0, 200, $height, $this->improps['event'], $this->improps['font'], 'Event');
|
|
|
+ imagefttext($this->image, 11, 0, 300, $height, $this->improps['eventtraining'], $this->improps['font'], 'Event + Training');
|
|
|
+
|
|
|
+ $height += $this->improps['lineheight'] * 2;
|
|
|
+ $namelength = 0;
|
|
|
+ asort($this->abbreviations);
|
|
|
+ foreach($this->abbreviations as $name) {
|
|
|
+ $namelength = strlen($name) > $namelength ? strlen($name) : $namelength;
|
|
|
+ }
|
|
|
+ $collength = ($namelength + 10) * 8;
|
|
|
+ $cols = floor( ($this->improps['width'] - 10) / $collength);
|
|
|
+ $col = 0;
|
|
|
+ $row = 0;
|
|
|
+ foreach($this->abbreviations as $abbrv => $name) {
|
|
|
+ imagefttext($this->image, 11, 0, $col * $collength + 10, $height + $row * $this->improps['lineheight'], $this->improps['black'], $this->improps['font'], $abbrv . ": " . $name);
|
|
|
+ $row = ($col + 1) >= $cols ? $row + 1 : $row;
|
|
|
+ $col = ($col + 1) >= $cols ? 0 : $col + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ $height += $this->improps['lineheight'] * ($row +2);
|
|
|
+ imagefttext($this->image, 11, 0, 10, $height, $this->improps['open'], $this->improps['font'], "Generated " . (new DateTime("now", new DateTimeZone('UTC')))->format('d.m.Y H:i:s') . "z" );
|
|
|
+ $this->image = imagecrop($this->image, ['x' => 0, 'y' => 0, 'width' => $this->improps['width'], 'height' => $height+5]);
|
|
|
+
|
|
|
+ return $this->image;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private function addBookingData($station, $date, $bookings, $x, $height) {
|
|
|
+ $count = 0;
|
|
|
+ foreach($bookings as $booking) {
|
|
|
+ if($booking['starts_at']->format('Y-m-d') == $date->format('Y-m-d')) {
|
|
|
+ $abbrv = substr($booking['firstname'], 0, 3) . substr($booking['lastname'], 0, 3); //TODO
|
|
|
+ $this->abbreviations[$abbrv] = $booking['firstname'] . " " . $booking['lastname'];
|
|
|
+ if($booking['training'] && $booking['event']) $color = $this->improps['eventtraining'];
|
|
|
+ elseif($booking['training']) $color = $this->improps['training'];
|
|
|
+ elseif($booking['event']) $color = $this->improps['event'];
|
|
|
+ else $color = $this->improps['black'];
|
|
|
+ imagefttext($this->image, 11, 0, $x, $height + $count * $this->improps['lineheight'], $color,
|
|
|
+ $this->improps['font'], $abbrv . " " . $booking['starts_at']->format('H') . "-" . $booking['ends_at']->format('H'));
|
|
|
+ $count++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if($count == 0 && !in_array($station, $this->optstations)) {
|
|
|
+ if(!$this->dayIsWanted($station, $date))
|
|
|
+ imagefttext($this->image, 10, 0, $x, $height, $this->improps['open'], $this->improps['font'], "Open");
|
|
|
+ else
|
|
|
+ imagefttext($this->image, 10, 0, $x, $height, $this->improps['wanted'], $this->improps['font'], "WANTED!");
|
|
|
+ }
|
|
|
+ return $count;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function fetchData() {
|
|
|
+ $url = sprintf("https://vatsim-germany.org/api/booking/atc/daterange/%s/%s", $this->displaydates[0]->format("d.m.Y"), $this->displaydates[count($this->displaydates) -1]->format("d.m.Y"));
|
|
|
+ $curl = curl_init();
|
|
|
+ curl_setopt($curl, CURLOPT_URL, $url ) ;
|
|
|
+ curl_setopt($curl, CURLOPT_HTTPHEADER, ["X-Requested-With: XMLHttpRequest"]);
|
|
|
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
|
+
|
|
|
+ // curl_setopt($curl, CURLOPT_VERBOSE, true);
|
|
|
+ // $verbose = fopen('php://temp', 'w+');
|
|
|
+ // curl_setopt($curl, CURLOPT_STDERR, $verbose);
|
|
|
+
|
|
|
+ $result = curl_exec($curl);
|
|
|
+
|
|
|
+ // if ($result === FALSE) {
|
|
|
+ // printf("cUrl error (#%d): %s<br>\n", curl_errno($curl),
|
|
|
+ // htmlspecialchars(curl_error($curl)));
|
|
|
+ // }
|
|
|
+ // rewind($verbose);
|
|
|
+ // $verboseLog = stream_get_contents($verbose);
|
|
|
+ // echo "Verbose information:\n<pre>", htmlspecialchars($verboseLog), "</pre>\n";
|
|
|
+
|
|
|
+ curl_close($curl);
|
|
|
+ return json_decode($result, true);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public static function EveryXDaysFromStartdate($startdate, $x = 1, $numberofdates = 1) {
|
|
|
+ $startdate = new DateTime($startdate, new DateTimeZone('UTC'));
|
|
|
+ $days = [];
|
|
|
+ while(count($days) < $numberofdates) {
|
|
|
+ if(new DateTime('today', new DateTimeZone('UTC')) <= $startdate) {
|
|
|
+ $days[] = clone($startdate);
|
|
|
+ }
|
|
|
+ $startdate->add(new DateInterval(sprintf("P%dD", $x)));
|
|
|
+ }
|
|
|
+ return $days;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function EveryXWeeksFromStartdate($startdate, $x = 1, $numberofdates = 1) {
|
|
|
+ return self::EveryXDaysFromStartdate($startdate, $x * 7, $numberofdates);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function EveryWeekday($dayoftheweek, $numberofdates = 1) {
|
|
|
+ $startdate = new DateTime($dayoftheweek, new DateTimeZone('UTC'));
|
|
|
+ $days = [];
|
|
|
+ while(count($days) < $numberofdates) {
|
|
|
+ $days[] = clone($startdate);
|
|
|
+ $startdate->add(new DateInterval("P7D"));
|
|
|
+ }
|
|
|
+ return $days;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function WholeWeek($startdate = "today") {
|
|
|
+ $days = [];
|
|
|
+ $startdate = new DateTime($startdate, new DateTimeZone('UTC'));
|
|
|
+ for($i = 0; $i < 7; $i++) {
|
|
|
+ $days[] = clone($startdate);
|
|
|
+ $startdate->add(new DateInterval("P1D"));
|
|
|
+ }
|
|
|
+ return $days;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function processWantedDays(...$args) {
|
|
|
+ if(count($args) % 2 == 1) throw new Exception("Even number of arguments required.");
|
|
|
+ $r = [];
|
|
|
+ for($i = 0; $i < count($args); $i += 2) {
|
|
|
+ foreach($args[$i] as $station) {
|
|
|
+ if(!isset($r[$station])) $r[$station] = [];
|
|
|
+ foreach($args[$i + 1] as $date) {
|
|
|
+ $r[$station][] = $date;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return $r;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function dayIsWanted($station, $date) {
|
|
|
+ if(!isset($this->wantedstations[$station])) return false;
|
|
|
+ foreach($this->wantedstations[$station] as $target) {
|
|
|
+ if($target->format("Y-m-d") == $date->format("Y-m-d")) return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|