first draft

This commit is contained in:
DavidBadura 2015-02-05 20:41:03 +01:00
parent bb89e0ea4d
commit fe02653016
11 changed files with 654 additions and 1 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
composer.phar
composer.lock
vendor/

26
.travis.yml Normal file
View File

@ -0,0 +1,26 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- hhvm
env:
matrix:
- PREFER_LOWEST="--prefer-lowest"
- PREFER_LOWEST=""
before_install:
- sudo add-apt-repository ppa:ultrafredde/ppa -y
- sudo apt-get update -qq
- sudo apt-get install -qq task
- task --version
before_script:
- composer self-update
- composer update --prefer-source $PREFER_LOWEST
matrix:
allow_failures:
- php: hhvm

View File

@ -1,2 +1,12 @@
# Taskwarrior # Taskwarrior
a php lib for taskwarrior
```php
$tw = new \DavidBadura\Taskwarrior\Taskwarrior();
$task = new \DavidBadura\Taskwarrior\Task();
$task->addTag('home');
$tw->save($task);
$tasks = $tw->filter('+home');
```

30
composer.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "davidbadura/taskwarrior",
"description": "a php lib for taskwarrior",
"keywords": [
"task",
"taskwarrior"
],
"type": "library",
"license": "MIT",
"authors": [
{
"name": "David Badura",
"email": "d.badura@gmx.de"
}
],
"require": {
"php": ">=5.4",
"symfony/process": "~2.3",
"jms/serializer": "0.16.*",
"symfony/filesystem": "~2.3"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"autoload": {
"psr-4": {
"DavidBadura\\Taskwarrior\\": "src"
}
}
}

9
phpunit.xml.dist Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="DavidBaduraTaskwarrior">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
</phpunit>

45
src/Modify.php Normal file
View File

@ -0,0 +1,45 @@
<?php
/**
* (c) SimpleThings GmbH
*/
namespace DavidBadura\Taskwarrior;
/**
* @author David Badura <badura@simplethings.de>
*/
class Modify
{
/**
* @var string
*/
private $description;
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* @param string $description
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* @param Task $task
* @return Modify
*/
public static function createFromTask(Task $task)
{
$modify = new self();
$modify->setDescription($task->getDescription());
return $modify;
}
}

57
src/Task.php Normal file
View File

@ -0,0 +1,57 @@
<?php
namespace DavidBadura\Taskwarrior;
use JMS\Serializer\Annotation as JMS;
/**
* @author David Badura <d.a.badura@gmail.com>
*/
class Task
{
/**
* @var string
*
* @JMS\Type(name="string")
*/
private $uuid;
/**
* @var string
*
* @JMS\Type(name="string")
*/
private $description;
/**
* @return string
*/
public function getUuid()
{
return $this->uuid;
}
/**
* @param string $uuid
*/
public function setUuid($uuid)
{
$this->uuid = $uuid;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* @param string $description
*/
public function setDescription($description)
{
$this->description = $description;
}
}

290
src/Taskwarrior.php Normal file
View File

@ -0,0 +1,290 @@
<?php
namespace DavidBadura\Taskwarrior;
use JMS\Serializer\SerializerBuilder;
use Symfony\Component\Process\ProcessBuilder;
use Symfony\Component\Filesystem\Filesystem;
/**
* @author David Badura <d.a.badura@gmail.com>
*/
class Taskwarrior
{
/**
* @var array
*/
private $rcOptions;
/**
* @var Task[]
*/
private $tasks = [];
/**
* @param string $taskrc
* @param string $taskData
* @param array $rcOptions
*/
public function __construct($taskrc = '~/.taskrc', $taskData = '~/.task', $rcOptions = [])
{
$this->rcOptions = array_merge(
array(
'rc:' . $taskrc,
'rc.data.location=' . $taskData,
'rc.json.array=false',
'rc.confirmation=no',
),
$rcOptions
);
}
/**
* @param Task $task
*/
public function save(Task $task)
{
if (!$task->getUuid()) {
$this->add($task);
} else {
$this->edit($task);
}
}
/**
* @param string $uuid
* @return Task
* @throws TaskwarriorException
*/
public function find($uuid)
{
if (isset($this->tasks[$uuid])) {
return $this->tasks[$uuid];
}
$tasks = $this->filter($uuid);
if (count($tasks) == 0) {
return null;
}
if (count($tasks) == 1) {
return $tasks[0];
}
throw new TaskwarriorException();
}
/**
* @param $filter
* @return Task[]
*/
public function filter($filter = '')
{
$result = $this->export($filter);
foreach ($result as $key => $task) {
if (isset($this->tasks[$task->getUuid()])) {
$result[$key] = $this->tasks[$task->getUuid()];
continue;
}
$this->tasks[$task->getUuid()] = $task;
}
return $result;
}
/**
* @param Task $task
*/
public function delete(Task $task)
{
// todo
}
/**
* @param Task $task
*/
public function done(Task $task)
{
// todo
}
/**
*
*/
public function clear()
{
$this->tasks = [];
}
/**
* @param $json
* @return string
* @throws TaskwarriorException
*/
private function import($json)
{
$fs = new Filesystem();
$file = tempnam(sys_get_temp_dir(), 'task') . '.json';
$fs->dumpFile($file, $json);
$output = $this->command('import', $file);
if (!preg_match('/([0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12})/', $output, $matches)) {
throw new TaskwarriorException();
}
return $matches[1];
}
/**
* @param string $filter
* @return Task[]
*/
private function export($filter = '')
{
$tasks = array();
$json = $this->command('export', $filter);
if (!$json) {
return $tasks;
}
$jsons = explode("\n", $json);
foreach ($jsons as $row) {
if (trim($row) == "") {
continue;
}
$serializer = SerializerBuilder::create()
->addDefaultHandlers()
->build();
$tasks[] = $serializer->deserialize($row, 'DavidBadura\Taskwarrior\Task', 'json');
}
return $tasks;
}
/**
* @param string $command
* @param string $filter
* @param array $options
* @return string
* @throws TaskwarriorException
*/
private function command($command, $filter = null, $options = array())
{
$builder = $this->createProcessBuilder();
if ($filter) {
$builder->add($filter);
}
$builder->add($command);
foreach ($options as $param) {
$builder->add($param);
}
$process = $builder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
throw new TaskwarriorException(
$process->getErrorOutput(),
$process->getExitCode(),
$process->getCommandLine()
);
}
return $process->getOutput();
}
/**
* @param Task $task
* @throws TaskwarriorException
*/
private function add(Task $task)
{
$json = $this->serializeTask($task);
$uuid = $this->import($json);
$task->setUuid($uuid);
$this->tasks[$uuid] = $task;
$this->update($task);
}
/**
* @param Task $task
*/
private function edit(Task $task)
{
$modify = Modify::createFromTask($task);
$options = $this->modifyOptions($modify);
$this->command('modify', $task->getUuid(), $options);
}
/**
* @param Task $task
*/
private function update(Task $task)
{
// todo
}
/**
* @return ProcessBuilder
*/
private function createProcessBuilder()
{
$builder = new ProcessBuilder();
foreach ($this->rcOptions as $option) {
$builder->add($option);
}
$builder->setPrefix('task');
$builder->setTimeout(360);
return $builder;
}
/**
*
* @param Task $task
* @return string
*/
private function serializeTask(Task $task)
{
$serializer = SerializerBuilder::create()
->addDefaultHandlers()
->build();
$result = $serializer->serialize($task, 'json');
return str_replace("\\/", "/", $result);
}
/**
* @param Modify $modify
* @return array
*/
private function modifyOptions(Modify $modify)
{
$array = [];
$array[] = $modify->getDescription();
return $array;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace DavidBadura\Taskwarrior;
use Exception;
/**
* @author David Badura <badura@simplethings.de>
*/
class TaskwarriorException extends \Exception
{
/**
* @var string
*/
private $command;
/**
* @param string $message
* @param int $code
* @param string $command
*/
public function __construct($message = "", $code = 0, $command = '')
{
parent::__construct($message, $code, null);
}
/**
* @return string
*/
public function getCommand()
{
return $this->command;
}
}

137
tests/TaskwarriorTest.php Normal file
View File

@ -0,0 +1,137 @@
<?php
namespace DavidBadura\Taskwarrior\Test;
use DavidBadura\Taskwarrior\Task;
use DavidBadura\Taskwarrior\Taskwarrior;
use Symfony\Component\Filesystem\Filesystem;
/**
* @author David Badura <badura@simplethings.de>
*/
class TaskwarriorTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Taskwarrior
*/
protected $taskwarrior;
public function setUp()
{
$this->taskwarrior = new Taskwarrior(__DIR__ . '/.taskrc', __DIR__ . '/.task');
}
public function tearDown()
{
$fs = new Filesystem();
$fs->remove(__DIR__ . '/.taskrc');
$fs->remove(__DIR__ . '/.task');
}
public function testEmpty()
{
$tasks = $this->taskwarrior->filter();
$this->assertEmpty($tasks);
}
public function testSaveTask()
{
$task = new Task();
$task->setDescription('foo');
$this->taskwarrior->save($task);
$this->taskwarrior->clear();
$this->assertNotEmpty($task->getUuid());
$result = $this->taskwarrior->find($task->getUuid());
$this->assertEquals($task, $result);
}
public function testFindFromCache()
{
$task = new Task();
$task->setDescription('foo');
$this->taskwarrior->save($task);
$result = $this->taskwarrior->find($task->getUuid());
$this->assertSame($task, $result);
}
public function testFilterFromCache()
{
$task = new Task();
$task->setDescription('foo');
$this->taskwarrior->save($task);
$result = $this->taskwarrior->filter($task->getUuid());
$this->assertSame($task, $result[0]);
}
public function testDontFind()
{
$task = $this->taskwarrior->find('56464asd46s4adas54da6');
$this->assertNull($task);
}
public function testDoubleSave()
{
$task = new Task();
$task->setDescription('foo');
$this->taskwarrior->save($task);
$this->assertNotEmpty($task->getUuid());
$uuid = $task->getUuid();
$this->taskwarrior->save($task);
$this->assertEquals($uuid, $task->getUuid());
$this->assertCount(1, $this->taskwarrior->filter());
$this->taskwarrior->clear();
$this->taskwarrior->save($task);
$this->assertEquals($uuid, $task->getUuid());
$this->assertCount(1, $this->taskwarrior->filter());
}
public function testFilterAll()
{
$task1 = new Task();
$task1->setDescription('foo1');
$task2 = new Task();
$task2->setDescription('foo2');
$this->taskwarrior->save($task1);
$this->taskwarrior->save($task2);
$this->assertCount(2, $this->taskwarrior->filter());
}
public function testModifyDescription()
{
$task1 = new Task();
$task1->setDescription('foo1');
$this->taskwarrior->save($task1);
$this->assertEquals('foo1', $task1->getDescription());
$task1->setDescription('bar1');
$this->taskwarrior->save($task1);
$this->taskwarrior->clear();
$result = $this->taskwarrior->find($task1->getUuid());
$this->assertEquals('bar1', $result->getDescription());
}
}

12
tests/bootstrap.php Normal file
View File

@ -0,0 +1,12 @@
<?php
if (!@include __DIR__ . '/../vendor/autoload.php') {
die(<<<'EOT'
You must set up the project dependencies, run the following commands:
wget http://getcomposer.org/composer.phar
php composer.phar install
EOT
);
}
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists');