first version that reads birthday reminder files
authorChristian Weiske <cweiske@cweiske.de>
Tue, 14 Jan 2014 16:46:51 +0000 (17:46 +0100)
committerChristian Weiske <cweiske@cweiske.de>
Tue, 14 Jan 2014 16:46:51 +0000 (17:46 +0100)
12 files changed:
.gitignore [new file with mode: 0644]
bin/bdrem.php [new file with mode: 0755]
data/bdrem.config.php.dist [new file with mode: 0644]
src/bdrem/Autoloader.php [new file with mode: 0644]
src/bdrem/Cli.php [new file with mode: 0644]
src/bdrem/Config.php [new file with mode: 0644]
src/bdrem/Event.php [new file with mode: 0644]
src/bdrem/Renderer/Console.php [new file with mode: 0644]
src/bdrem/Source/Bdf.php [new file with mode: 0644]
tests/bdrem/EventTest.php [new file with mode: 0644]
tests/bootstrap.php [new file with mode: 0644]
tests/phpunit.xml [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..bb3b293
--- /dev/null
@@ -0,0 +1 @@
+/data/bdrem.config.php
diff --git a/bin/bdrem.php b/bin/bdrem.php
new file mode 100755 (executable)
index 0000000..4965c3d
--- /dev/null
@@ -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 (file)
index 0000000..522a477
--- /dev/null
@@ -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 (file)
index 0000000..093b1b0
--- /dev/null
@@ -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 (file)
index 0000000..5ab5ec8
--- /dev/null
@@ -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 (file)
index 0000000..076cd04
--- /dev/null
@@ -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 (file)
index 0000000..65e4b9b
--- /dev/null
@@ -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 (file)
index 0000000..2cd185f
--- /dev/null
@@ -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 (file)
index 0000000..e8208eb
--- /dev/null
@@ -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 (file)
index 0000000..3b6c903
--- /dev/null
@@ -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 (file)
index 0000000..9c19b2a
--- /dev/null
@@ -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 (file)
index 0000000..a1dc208
--- /dev/null
@@ -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>