diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rwxr-xr-x | bin/bdrem.php | 11 | ||||
| -rw-r--r-- | data/bdrem.config.php.dist | 5 | ||||
| -rw-r--r-- | src/bdrem/Autoloader.php | 22 | ||||
| -rw-r--r-- | src/bdrem/Cli.php | 21 | ||||
| -rw-r--r-- | src/bdrem/Config.php | 43 | ||||
| -rw-r--r-- | src/bdrem/Event.php | 117 | ||||
| -rw-r--r-- | src/bdrem/Renderer/Console.php | 39 | ||||
| -rw-r--r-- | src/bdrem/Source/Bdf.php | 48 | ||||
| -rw-r--r-- | tests/bdrem/EventTest.php | 139 | ||||
| -rw-r--r-- | tests/bootstrap.php | 5 | ||||
| -rw-r--r-- | tests/phpunit.xml | 8 |
12 files changed, 459 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb3b293 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/data/bdrem.config.php diff --git a/bin/bdrem.php b/bin/bdrem.php new file mode 100755 index 0000000..4965c3d --- /dev/null +++ b/bin/bdrem.php @@ -0,0 +1,11 @@ +#!/usr/bin/env php +<?php +namespace bdrem; + +if (file_exists(__DIR__ . '/../src/bdrem/Autoloader.php')) { + require_once __DIR__ . '/../src/bdrem/Autoloader.php'; + Autoloader::register(); +} +$cli = new Cli(); +$cli->run(); +?> diff --git a/data/bdrem.config.php.dist b/data/bdrem.config.php.dist new file mode 100644 index 0000000..522a477 --- /dev/null +++ b/data/bdrem.config.php.dist @@ -0,0 +1,5 @@ +<?php +$source = array('Bdf', '/path/to/birthday.bdf'); +$daysBefore = 3; +$daysAfter = 14; +?> diff --git a/src/bdrem/Autoloader.php b/src/bdrem/Autoloader.php new file mode 100644 index 0000000..093b1b0 --- /dev/null +++ b/src/bdrem/Autoloader.php @@ -0,0 +1,22 @@ +<?php +namespace bdrem; + +class Autoloader +{ + public function load($class) + { + $file = strtr($class, '_\\', '//') . '.php'; + if (stream_resolve_include_path($file)) { + require $file; + } + } + + public static function register() + { + set_include_path( + get_include_path() . PATH_SEPARATOR . __DIR__ . '/../' + ); + spl_autoload_register(array(new self(), 'load')); + } +} +?>
\ No newline at end of file diff --git a/src/bdrem/Cli.php b/src/bdrem/Cli.php new file mode 100644 index 0000000..5ab5ec8 --- /dev/null +++ b/src/bdrem/Cli.php @@ -0,0 +1,21 @@ +<?php +namespace bdrem; + +class Cli +{ + public function run() + { + $cfg = new Config(); + $cfg->load(); + $source = $cfg->loadSource(); + + $arEvents = $source->getEvents( + date('Y-m-d'), $cfg->daysBefore, $cfg->daysAfter + ); + usort($arEvents, '\\bdrem\\Event::compare'); + + $r = new Renderer_Console(); + echo $r->render($arEvents); + } +} +?> diff --git a/src/bdrem/Config.php b/src/bdrem/Config.php new file mode 100644 index 0000000..076cd04 --- /dev/null +++ b/src/bdrem/Config.php @@ -0,0 +1,43 @@ +<?php +namespace bdrem; + +class Config +{ + public $source; + public $daysBefore; + public $daysAfter; + + public function load() + { + $f = __DIR__ . '/../../data/bdrem.config.php'; + if (file_exists($f)) { + return $this->loadFile($f); + } + + throw new \Exception('No config file found'); + } + + protected function loadFile($filename) + { + include $filename; + $this->source = $source; + $this->daysBefore = $daysBefore; + $this->daysAfter = $daysAfter; + } + + public function loadSource() + { + if ($this->source === null) { + throw new \Exception('No source defined'); + } + + $settings = $this->source; + $class = '\\bdrem\\Source_' . array_shift($settings); + + return new $class($settings[0]); + //$rm = new \ReflectionMethod($class, '__construct'); + //return $rm->invokeArgs(null, $settings); + //return call_user_func_array($class . '::__construct', $settings); + } +} +?> diff --git a/src/bdrem/Event.php b/src/bdrem/Event.php new file mode 100644 index 0000000..65e4b9b --- /dev/null +++ b/src/bdrem/Event.php @@ -0,0 +1,117 @@ +<?php +namespace bdrem; + +class Event +{ + /** + * Title of the event or name of the person that has the event + */ + public $title; + + /** + * Type of the event, e.g. "birthday" + */ + public $type; + + /** + * Date of the event. + * ???? as year is allowed + * + * @var string YYYY-MM-DD + */ + public $date; + + /** + * Which repetition this is + * + * @var integer + */ + public $age; + + /** + * Number of days until the event (positive) or since the event (negative) + * + * @var integer + */ + public $days; + + + + public function __construct($title = null, $type = null, $date = null) + { + $this->title = $title; + $this->type = $type; + $this->date = $date; + } + + /** + * Checks if the event's date is within the given date. + * Also calculates the age and days since the event. + * + * @return boolean True if the event's date is within the given range + */ + public function isWithin($strDate, $nDaysBefore, $nDaysAfter) + { + list($rYear, $rMonth, $rDay) = explode('-', $strDate); + list($eYear, $eMonth, $eDay) = explode('-', $this->date); + + if ($rMonth == $eMonth && $rDay == $eDay) { + $this->days = 0; + if ($eYear == '????') { + $this->age = null; + } else { + $this->age = $rYear - $eYear; + } + return true; + } + + $yearOffset = 0; + if ($eMonth < 3 && $rMonth > 10) { + $yearOffset = 1; + } else if ($eMonth > 10 && $rMonth < 3) { + $yearOffset = -1; + } + + $rD = new \DateTime($strDate); + $eD = new \DateTime(($rYear + $yearOffset) . '-' . $eMonth . '-' . $eDay); + + $nDiff = (int) $rD->diff($eD)->format('%r%a'); + + $this->days = $nDiff; + if ($eYear == '????') { + $this->age = null; + } else { + $this->age = $rYear - $eYear + $yearOffset; + } + + if ($nDiff > 0) { + return $nDiff <= $nDaysAfter; + } else { + return -$nDiff <= $nDaysBefore; + } + + return false; + } + + /** + * @return integer x < 0: e1 is less than e2 + * x > 0: e1 is larger than e2 + */ + public static function compare(Event $e1, Event $e2) + { + list($e1Year, $e1Month, $e1Day) = explode('-', $e1->date); + list($e2Year, $e2Month, $e2Day) = explode('-', $e2->date); + + if ($e1Month < 3 && $e2Month > 10) { + return 1; + } else if ($e1Month > 10 && $e2Month < 3) { + return -1; + } else if ($e1Month != $e2Month) { + return $e1Month - $e2Month; + } else if ($e1Day != $e2Day) { + return $e1Day - $e2Day; + } + return strcmp($e1->title, $e2->title); + } +} +?> diff --git a/src/bdrem/Renderer/Console.php b/src/bdrem/Renderer/Console.php new file mode 100644 index 0000000..2cd185f --- /dev/null +++ b/src/bdrem/Renderer/Console.php @@ -0,0 +1,39 @@ +<?php +namespace bdrem; + +class Renderer_Console +{ + public function render($arEvents) + { + $s = "Days Age Name Event Date\n"; + $s .= "---- --- ---------------------------------------- -------------------- ----------\n"; + foreach ($arEvents as $event) { + $s .= sprintf( + "%3d %4s %s %s %s\n", + $event->days, + $event->age, + $this->str_pad($event->title, 40), + $this->str_pad($event->type, 20), + $event->date + ); + } + return $s; + } + + public function str_pad( + $input, $pad_length, $pad_string = ' ', $pad_type = STR_PAD_RIGHT + ) { + $l = mb_strlen($input, 'utf-8'); + if ($l >= $pad_length) { + return $input; + } + + $p = str_repeat($pad_string, $pad_length - $l); + if ($pad_type == STR_PAD_RIGHT) { + return $input . $p; + } else { + return $p . $input; + } + } +} +?> diff --git a/src/bdrem/Source/Bdf.php b/src/bdrem/Source/Bdf.php new file mode 100644 index 0000000..e8208eb --- /dev/null +++ b/src/bdrem/Source/Bdf.php @@ -0,0 +1,48 @@ +<?php +namespace bdrem; + +/** + * Reads birthday reminder 2's birthday files (.bdf). + */ +class Source_Bdf +{ + protected $filename; + + public function __construct($filename) + { + $this->filename = $filename; + if (!file_exists($this->filename)) { + throw new \Exception( + 'Birthday file does not exist: ' . $this->filename + ); + } + } + + /** + * @param string $strDate Date the events shall be found for, YYYY-MM-DD + */ + public function getEvents($strDate, $nDaysBefore, $nDaysAfter) + { + $x = simplexml_load_file($this->filename); + + $arEvents = array(); + foreach ($x->content->person as $xPerson) { + $date = implode( + '-', + array_reverse( + explode('.', (string) $xPerson->date) + ) + ); + $event = new Event( + (string) $xPerson->name, + (string) $xPerson->event, + $date + ); + if ($event->isWithin($strDate, $nDaysBefore, $nDaysAfter)) { + $arEvents[] = $event; + } + } + return $arEvents; + } +} +?> diff --git a/tests/bdrem/EventTest.php b/tests/bdrem/EventTest.php new file mode 100644 index 0000000..3b6c903 --- /dev/null +++ b/tests/bdrem/EventTest.php @@ -0,0 +1,139 @@ +<?php +namespace bdrem; + +class EventTest extends \PHPUnit_Framework_TestCase +{ + public function testIsWithinSameDaySameYear() + { + $event = new Event('Amy', 'birthday', '1995-02-24'); + $this->assertTrue($event->isWithin('1995-02-24', 0, 0)); + $this->assertTrue($event->isWithin('1995-02-24', 3, 7)); + } + + public function testIsWithinSameDayDifferentYear() + { + $event = new Event('Amy', 'birthday', '1995-02-24'); + $this->assertTrue($event->isWithin('2019-02-24', 0, 0)); + $this->assertTrue($event->isWithin('1996-02-24', 3, 7)); + } + + public function testIsWithinOneDayAfterSameYear() + { + $event = new Event('Amy', 'birthday', '1995-02-24'); + $this->assertFalse($event->isWithin('1995-02-23', 0, 0)); + $this->assertTrue($event->isWithin('1995-02-23', 3, 7)); + } + + public function testIsWithinOneDayBeforeSameYear() + { + $event = new Event('Amy', 'birthday', '1995-02-24'); + $this->assertFalse($event->isWithin('1995-02-25', 0, 0)); + $this->assertTrue($event->isWithin('1995-02-25', 3, 7)); + } + + public function testIsWithinOneDayBeforeDifferentYear() + { + $event = new Event('Amy', 'birthday', '1995-02-24'); + $this->assertFalse($event->isWithin('1999-02-25', 0, 0)); + $this->assertTrue($event->isWithin('1990-02-25', 3, 7)); + } + + public function testIsWithinThreeDaysBeforeSameYear() + { + $event = new Event('Amy', 'birthday', '1995-02-24'); + $this->assertFalse($event->isWithin('1999-02-27', 0, 0)); + $this->assertFalse($event->isWithin('1999-02-27', 1, 0)); + $this->assertFalse($event->isWithin('1999-02-27', 2, 0)); + $this->assertTrue( $event->isWithin('1990-02-27', 3, 0)); + + $this->assertFalse($event->isWithin('1999-02-27', 0, 3)); + } + + public function testIsWithinYearOverflowAfter() + { + $event = new Event('Amy', 'birthday', '1995-01-01'); + $this->assertTrue($event->isWithin('2019-12-31', 0, 1)); + $this->assertFalse($event->isWithin('1996-12-30', 0, 1)); + } + + public function testIsWithinYearOverflowBefore() + { + $event = new Event('Amy', 'birthday', '1995-12-30'); + $this->assertTrue($event->isWithin('2019-01-02', 3, 0)); + $this->assertFalse($event->isWithin('1996-01-02', 2, 0)); + $this->assertTrue($event->isWithin('1996-01-01', 2, 0)); + } + + public function testCompareDifferentMonths() + { + $this->assertLessThan( + 0, + Event::compare( + new Event('Amy', 'birthday', '2013-05-10'), + new Event('Bob', 'birthday', '2013-06-10') + ) + ); + $this->assertGreaterThan( + 0, + Event::compare( + new Event('Amy', 'birthday', '2013-10-10'), + new Event('Bob', 'birthday', '2013-08-10') + ) + ); + } + + public function testCompareDifferentMonthsYearOverflow() + { + $this->assertGreaterThan( + 0, + Event::compare( + new Event('Amy', 'birthday', '2013-01-10'), + new Event('Bob', 'birthday', '2013-12-10') + ) + ); + $this->assertLessThan( + 0, + Event::compare( + new Event('Amy', 'birthday', '2013-12-10'), + new Event('Bob', 'birthday', '2013-02-10') + ) + ); + } + + public function testCompareDifferentDays() + { + $this->assertLessThan( + 0, + Event::compare( + new Event('Amy', 'birthday', '1950-05-10'), + new Event('Bob', 'birthday', '2013-05-11') + ) + ); + $this->assertGreaterThan( + 0, + Event::compare( + new Event('Amy', 'birthday', '2013-10-20'), + new Event('Bob', 'birthday', '1992-10-02') + ) + ); + } + + public function testCompareSameDay() + { + $this->assertLessThan( + 0, + Event::compare( + new Event('Amy', 'birthday', '1950-05-10'), + new Event('Bob', 'birthday', '2013-05-10') + ) + ); + $this->assertGreaterThan( + 0, + Event::compare( + new Event('Bob', 'birthday', '1992-10-02'), + new Event('Amy', 'birthday', '2013-10-02') + ) + ); + } +} +?> diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..9c19b2a --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,5 @@ +<?php +namespace bdrem; +require_once __DIR__ . '/../src/bdrem/Autoloader.php'; +Autoloader::register(); +?> diff --git a/tests/phpunit.xml b/tests/phpunit.xml new file mode 100644 index 0000000..a1dc208 --- /dev/null +++ b/tests/phpunit.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8" ?> +<phpunit strict="true" colors="true" bootstrap="bootstrap.php"> + <filter> + <whitelist addUncoveredFilesFromWhitelist="false"> + <directory suffix=".php">../src/</directory> + </whitelist> + </filter> +</phpunit> |
