commit 298f6e6759b9b42769474e83391df610463f2b83 Author: Daniel Date: Tue Sep 28 13:27:39 2021 +0200 Creating GIT Repo diff --git a/FRAShuttle.jpg.php b/FRAShuttle.jpg.php new file mode 100644 index 0000000..f86dc87 --- /dev/null +++ b/FRAShuttle.jpg.php @@ -0,0 +1,10 @@ +createImage(); + +header("Content-Type: image/jpg"); +imagejpeg($image); \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b36ef19 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +VATSIM PHP Scheduler, using VATSIM GERMANY API. + +It is recommended to set up a separate PHP file for each overview you would like to create. Alternatively one file can be created that outputs images to files using a cronjob. +If multiple overviews use the same stations, optional stations or wanted stations, a central config file might be useful. + +A new Overview is created by making a new instance of the Schedule class: + +```php +$schedule = new Schedule($displaydates, $stations, $optstations, $wantedstations, $darkmode); +``` + +* `$displaydates` is an Array that should be created with one of the helper functions: + * `Schedule::EveryXDaysFromStartdate (String startdate, int x, int numberofdays = 1)` - Returns an array of DateTime objects. For regularly recurring events. Define a startdate and an interval in days for the event. numberofdays defines how many upcoming days will be returned. + * `EveryXWeeksFromStartdate (String startdate, int x, int numberofdays = 1)` - Returns an array of DateTime objects. For regularly recurring events. Define a startdate and an interval in weeks for the event. numberofdays defines how many upcoming days will be returned. This is just an alias for EveryXDaysFromStardate with the input for x being x*7. + * `EveryWeekday (String weekday, int numberofdays)` - Returns a list of datetime objects. This can be used for weekly events. Define a day of the week (such as "Wednesday") and the number of days to be displayed. + * `WholeWeek (int shift = 0)` - Returns a list of 7 datetime objects for consecutive days. Will start with today + shift days. +* `$stations` - an array of the stations that are to be displayed (including optional ones!). The String `--` can be used to display a visual seperator. +* `$optstations` - an array of the stations that are only to be displayed when there is a booking for that station. Defaults to `[]`. +* `$wantedstations` - A 2-dimensional array of stations and dates that should show "Wanted!" instead of "Open" when no booking is made. Defaults to `[]`. A helper function to create this array is provided: + * `Schedule::processWantedDays(array stations, array dates, ...)`: In pairs of stations and dates each, provide as many pairs as needed. Stations should just be a plain array of stations, dates should once again be an array of DateTime objects, ideally using the helper functions described above. +* `$darkmode` - Whether to display a dark or light background. Defaults to `false`. The attached `darkmode.php` provides a way to set a cookie for the darkmode which can be used to feed this value. + +The image will be creted calling the `createImage` method. +```php + $image = $schedule->createImage(); + imagejpg($image); +``` \ No newline at end of file diff --git a/Schedule.php b/Schedule.php new file mode 100644 index 0000000..38b2321 --- /dev/null +++ b/Schedule.php @@ -0,0 +1,265 @@ +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
\n", curl_errno($curl), + // htmlspecialchars(curl_error($curl))); + // } + // rewind($verbose); + // $verboseLog = stream_get_contents($verbose); + // echo "Verbose information:\n
", htmlspecialchars($verboseLog), "
\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; + } +} \ No newline at end of file diff --git a/Tuesday.jpg.php b/Tuesday.jpg.php new file mode 100644 index 0000000..5d963d2 --- /dev/null +++ b/Tuesday.jpg.php @@ -0,0 +1,10 @@ +createImage(); + +header("Content-Type: image/jpg"); +imagejpeg($image); \ No newline at end of file diff --git a/UbuntuMono-Regular.ttf b/UbuntuMono-Regular.ttf new file mode 100644 index 0000000..c8add8e Binary files /dev/null and b/UbuntuMono-Regular.ttf differ diff --git a/Woche.jpg.php b/Woche.jpg.php new file mode 100644 index 0000000..a5f0a42 --- /dev/null +++ b/Woche.jpg.php @@ -0,0 +1,10 @@ +createImage(); + +header("Content-Type: image/jpg"); +imagejpeg($image); \ No newline at end of file diff --git a/Woche2.jpg.php b/Woche2.jpg.php new file mode 100644 index 0000000..e6b2649 --- /dev/null +++ b/Woche2.jpg.php @@ -0,0 +1,10 @@ +createImage(); + +header("Content-Type: image/jpg"); +imagejpeg($image); \ No newline at end of file diff --git a/config.php b/config.php new file mode 100644 index 0000000..4c91fca --- /dev/null +++ b/config.php @@ -0,0 +1,128 @@ + + + + + + + + PHP Vatsim Scheduler: Darkmode + + +

PHP Vatsim Scheduler: Darkmode

+
Darkmode ist
+
+ + +
+ + \ No newline at end of file diff --git a/test.jpg.php b/test.jpg.php new file mode 100644 index 0000000..435d93c --- /dev/null +++ b/test.jpg.php @@ -0,0 +1,91 @@ +"; + +//$sched = new Schedule(Schedule::EveryXDaysFromStartdate("26.02.2021", 4, 2), $stations); +$wanted = Schedule::processWantedDays($wantedstns, Schedule::EveryWeekday("Tuesday", 5), $wantedstns, Schedule::EveryXWeeksFromStartdate("2021-10-08", 4, 2)); +//var_dump($wanted); +//exit; +$sched = new Schedule(Schedule::WholeWeek("today"), $stations, $optional, $wanted, false); +$image = $sched->createImage(); + +header("Content-Type: image/jpg"); +imagejpeg($image); + +//imagejpeg($image, __DIR__ . "/test.jpg"); +//?/>
\ No newline at end of file