Skip to content

Commit

Permalink
Merge pull request #93 from AydinHassan/workflow-result
Browse files Browse the repository at this point in the history
WIP: Return Result Object
  • Loading branch information
ddeboer committed Jul 23, 2014
2 parents 4b97b3e + 7a6e146 commit d58d326
Show file tree
Hide file tree
Showing 5 changed files with 348 additions and 8 deletions.
51 changes: 49 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Documentation
* [Installation](#installation)
* [Usage](#usage)
* [The workflow](#the-workflow)
* [The workflow result](#the-workflow-result)
* [Readers](#readers)
- [ArrayReader](#arrayreader)
- [CsvReader](#csvreader)
Expand Down Expand Up @@ -103,7 +104,8 @@ Each data import revolves around the workflow and takes place along the followin
3. Optionally, add [filters](#filters), item converters and
[value converters](#value-converters) to the workflow.
4. Process the workflow. This will read the data from the reader, filter and
convert the data, and write the output to each of the writers.
convert the data, and write the output to each of the writers. The process method also
returns a `Result` object which contains various information about the import.

In other words, the workflow acts as a [mediator](#http://en.wikipedia.org/wiki/Mediator_pattern)
between a reader and one or more writers, filters and converters.
Expand All @@ -121,14 +123,59 @@ use Ddeboer\DataImport\Filter;

$reader = new Reader\...;
$workflow = new Workflow($reader, $logger);
$workflow
$result = $workflow
->addWriter(new Writer\...())
->addWriter(new Writer\...())
->addFilter(new Filter\CallbackFilter(...))
->setSkipItemOnFailure(true)
->process()
;
```
### The workflow Result

The Workflow Result object exposes various methods which you can use to decide what to do after an import.
The result will be an instance of `Ddeboer\DataImport\Result`. It is automatically created and populated by the
`Workflow`. It will be returned to you after calling the `process()` method on the `Workflow`

The `Result` provides the following methods:

```php
//the name of the import - which is an optional 3rd parameter to
//the Workflow class. Returns null by default.
public function getName();

//DateTime instance created at the start of the import.
public function getStartTime();

//DateTime instance created at the end of the import.
public function getEndTime();

//DateInterval instance. Diff off the start + end times.
public function getElapsed();

//Count of exceptions which caught by the Workflow.
public function getErrorCount();

//Count of processed items minus the count of exceptions caught.
public function getSuccessCount();

//Count of items processed
//This will not include any filtered items or items which fail conversion.
public function getTotalProcessedCount();

//bool to indicate whether any exceptions were caught.
public function hasErrors();

//An array of exceptions caught by the Workflow.
public function getExceptions();

```

Example use cases:
* You want to send an e-mail with the results of the import
* You want to send a Text alert if a particular file failed
* You want to move an import file to a failed directory if there were errors
* You want to log how long imports are taking

### Readers

Expand Down
150 changes: 150 additions & 0 deletions src/Ddeboer/DataImport/Result.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

namespace Ddeboer\DataImport;

use DateTime;
use DateInterval;
use Ddeboer\DataImport\Exception\ExceptionInterface;

/**
* Simple Container for Workflow
* Results
*
* @author Aydin Hassan <[email protected]>
*/
class Result
{

/**
* Identifier given to the import/export
*
* @var string
*/
protected $name;

/**
* @var DateTime
*/
protected $startTime;

/**
* @var DateTime
*/
protected $endTime;

/**
* @var DateInterval
*/
protected $elapsed;

/**
* @var int Number Of Errors
*/
protected $errorCount = 0;

/**
* @var int Number of Successes
*/
protected $successCount = 0;

/**
* @var int Total Rows Processed
*/
protected $totalProcessedCount = 0;

/**
* @var ExceptionInterface[]
*/
protected $exceptions;

/**
* @param $name
* @param DateTime $startTime
* @param DateTime $endTime
* @param $totalCount
* @param ExceptionInterface[] $exceptions
*/
public function __construct($name, DateTime $startTime, DateTime $endTime, $totalCount, array $exceptions = array())
{
$this->name = $name;
$this->startTime = $startTime;
$this->endTime = $endTime;
$this->elapsed = $startTime->diff($endTime);
$this->totalProcessedCount = $totalCount;
$this->errorCount = count($exceptions);
$this->successCount = $totalCount - $this->errorCount;
$this->exceptions = $exceptions;
}

/**
* @return string
*/
public function getName()
{
return $this->name;
}

/**
* @return DateTime
*/
public function getStartTime()
{
return $this->startTime;
}

/**
* @return DateTime
*/
public function getEndTime()
{
return $this->endTime;
}

/**
* @return DateInterval
*/
public function getElapsed()
{
return $this->elapsed;
}

/**
* @return int
*/
public function getErrorCount()
{
return $this->errorCount;
}

/**
* @return int
*/
public function getSuccessCount()
{
return $this->successCount;
}

/**
* @return int
*/
public function getTotalProcessedCount()
{
return $this->totalProcessedCount;
}

/**
* @return bool
*/
public function hasErrors()
{
return count($this->exceptions) > 0;
}

/**
* @return ExceptionInterface[]
*/
public function getExceptions()
{
return $this->exceptions;
}
}
36 changes: 30 additions & 6 deletions src/Ddeboer/DataImport/Workflow.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use Ddeboer\DataImport\ValueConverter\ValueConverterInterface;
use Ddeboer\DataImport\ItemConverter\ItemConverterInterface;

use DateTime;

/**
* A mediator between a reader and one or more writers and converters
*
Expand Down Expand Up @@ -77,14 +79,33 @@ class Workflow
*/
protected $afterConversionFilters = array();

/**
* Identifier for the Import/Export
*
* @var string|null
*/
protected $name = null;

/**
* Construct a workflow
*
* @param ReaderInterface $reader
* @param LoggerInterface $logger
* @param string $name
*/
public function __construct(ReaderInterface $reader, LoggerInterface $logger = null)
public function __construct(ReaderInterface $reader, LoggerInterface $logger = null, $name = null)
{

if (null !== $name && !is_string($name)) {
throw new \InvalidArgumentException(
sprintf(
"Name identifier should be a string. Given: '%s'",
(is_object($name) ? get_class($name) : gettype($name))
)
);
}

$this->name = $name;
$this->reader = $reader;
$this->logger = $logger ? $logger : new NullLogger();
$this->filters = new \SplPriorityQueue();
Expand Down Expand Up @@ -204,11 +225,14 @@ public function addMapping($fromField, $toField)
* converters.
* 5. Write the item to each of the writers.
*
* @return int Number of items processed
* @throws ExceptionInterface
* @return Result Object Containing Workflow Results
*/
public function process()
{
$count = 0;
$count = 0;
$exceptions = array();
$startTime = new DateTime;

// Prepare writers
foreach ($this->writers as $writer) {
Expand All @@ -217,7 +241,6 @@ public function process()

// Read all items
foreach ($this->reader as $item) {

try {
// Apply filters before conversion
if (!$this->filterItem($item, $this->filters)) {
Expand All @@ -238,22 +261,23 @@ public function process()
$writer->writeItem($convertedItem, $item);
}

$count++;
} catch(ExceptionInterface $e) {
if ($this->skipItemOnFailure) {
$exceptions[] = $e;
$this->logger->error($e->getMessage());
} else {
throw $e;
}
}
$count++;
}

// Finish writers
foreach ($this->writers as $writer) {
$writer->finish();
}

return $count;
return new Result($this->name, $startTime, new DateTime, $count, $exceptions);
}

/**
Expand Down
63 changes: 63 additions & 0 deletions tests/Ddeboer/DataImport/Tests/ResultTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Ddeboer\DataImport\Tests;

use Ddeboer\DataImport\Result;

/**
* Tests For Workflow Result
*
* @author Aydin Hassan <[email protected]>
*/
class ResultTest extends \PHPUnit_Framework_TestCase
{
public function testResultName()
{
$result = new Result('export', new \DateTime, new \DateTime, 10);
$this->assertSame('export', $result->getName());
}

public function testResultCounts()
{
$result = new Result('export', new \DateTime, new \DateTime, 10);
$this->assertSame(10, $result->getTotalProcessedCount());
$this->assertSame(10, $result->getSuccessCount());
$this->assertSame(0, $result->getErrorCount());
$result = new Result('export', new \DateTime, new \DateTime, 10, array(new \Exception, new \Exception));
$this->assertSame(10, $result->getTotalProcessedCount());
$this->assertSame(8, $result->getSuccessCount());
$this->assertSame(2, $result->getErrorCount());

}

public function testDates()
{
$startDate = new \DateTime("22-07-2014 22:00");
$endDate = new \DateTime("22-07-2014 23:30");

$result = new Result('export', $startDate, $endDate, 10);

$this->assertSame($startDate, $result->getStartTime());
$this->assertSame($endDate, $result->getEndTime());
$this->assertInstanceOf('DateInterval', $result->getElapsed());
}

public function testHasErrorsReturnsTrueIfAnyExceptions()
{
$result = new Result('export', new \DateTime, new \DateTime, 10, array(new \Exception, new \Exception));
$this->assertTrue($result->hasErrors());
}

public function testHasErrorsReturnsFalseIfNoExceptions()
{
$result = new Result('export', new \DateTime, new \DateTime, 10);
$this->assertFalse($result->hasErrors());
}

public function testGetExceptions()
{
$exceptions = array(new \Exception, new \Exception);
$result = new Result('export', new \DateTime, new \DateTime, 10, $exceptions);
$this->assertSame($exceptions, $result->getExceptions());
}
}
Loading

0 comments on commit d58d326

Please sign in to comment.