summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Api/BoardApi.php8
-rw-r--r--app/Controller/BoardAjaxController.php2
-rw-r--r--app/Controller/BoardViewController.php8
-rw-r--r--app/Filter/BaseFilter.php3
-rw-r--r--app/Formatter/BaseFormatter.php17
-rw-r--r--app/Formatter/BoardColumnFormatter.php79
-rw-r--r--app/Formatter/BoardFormatter.php19
-rw-r--r--app/Formatter/BoardSwimlaneFormatter.php105
-rw-r--r--app/Formatter/BoardTaskFormatter.php80
-rw-r--r--app/Model/BoardModel.php60
-rw-r--r--app/Model/TaskFinderModel.php20
-rw-r--r--app/Template/board/table_column.php4
-rw-r--r--app/functions.php26
-rw-r--r--tests/units/Formatter/BoardFormatterTest.php311
-rw-r--r--tests/units/FunctionTest.php21
-rw-r--r--tests/units/Model/BoardTest.php121
16 files changed, 667 insertions, 217 deletions
diff --git a/app/Api/BoardApi.php b/app/Api/BoardApi.php
index aa5942af..70f21c0e 100644
--- a/app/Api/BoardApi.php
+++ b/app/Api/BoardApi.php
@@ -2,6 +2,8 @@
namespace Kanboard\Api;
+use Kanboard\Formatter\BoardFormatter;
+
/**
* Board API controller
*
@@ -13,6 +15,10 @@ class BoardApi extends BaseApi
public function getBoard($project_id)
{
$this->checkProjectPermission($project_id);
- return $this->boardModel->getBoard($project_id);
+
+ return BoardFormatter::getInstance($this->container)
+ ->withProjectId($project_id)
+ ->withQuery($this->taskFinderModel->getExtendedQuery())
+ ->format();
}
}
diff --git a/app/Controller/BoardAjaxController.php b/app/Controller/BoardAjaxController.php
index 24914671..9b721f06 100644
--- a/app/Controller/BoardAjaxController.php
+++ b/app/Controller/BoardAjaxController.php
@@ -134,7 +134,7 @@ class BoardAjaxController extends BaseController
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
'swimlanes' => $this->taskLexer
->build($this->userSession->getFilters($project_id))
- ->format(BoardFormatter::getInstance($this->container)->setProjectId($project_id))
+ ->format(BoardFormatter::getInstance($this->container)->withProjectId($project_id))
));
}
}
diff --git a/app/Controller/BoardViewController.php b/app/Controller/BoardViewController.php
index 496fa995..97c99d11 100644
--- a/app/Controller/BoardViewController.php
+++ b/app/Controller/BoardViewController.php
@@ -30,7 +30,11 @@ class BoardViewController extends BaseController
$this->response->html($this->helper->layout->app('board/view_public', array(
'project' => $project,
- 'swimlanes' => $this->boardModel->getBoard($project['id']),
+ 'swimlanes' => BoardFormatter::getInstance($this->container)
+ ->withProjectId($project['id'])
+ ->withQuery($this->taskFinderModel->getExtendedQuery())
+ ->format()
+ ,
'title' => $project['name'],
'description' => $project['description'],
'no_layout' => true,
@@ -59,7 +63,7 @@ class BoardViewController extends BaseController
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
'swimlanes' => $this->taskLexer
->build($search)
- ->format(BoardFormatter::getInstance($this->container)->setProjectId($project['id']))
+ ->format(BoardFormatter::getInstance($this->container)->withProjectId($project['id']))
)));
}
}
diff --git a/app/Filter/BaseFilter.php b/app/Filter/BaseFilter.php
index 79a664be..e029f4e1 100644
--- a/app/Filter/BaseFilter.php
+++ b/app/Filter/BaseFilter.php
@@ -43,8 +43,7 @@ abstract class BaseFilter
*/
public static function getInstance($value = null)
{
- $self = new static($value);
- return $self;
+ return new static($value);
}
/**
diff --git a/app/Formatter/BaseFormatter.php b/app/Formatter/BaseFormatter.php
index a9f0ad15..89c48437 100644
--- a/app/Formatter/BaseFormatter.php
+++ b/app/Formatter/BaseFormatter.php
@@ -3,8 +3,8 @@
namespace Kanboard\Formatter;
use Kanboard\Core\Base;
-use Kanboard\Core\Filter\FormatterInterface;
use PicoDb\Table;
+use Pimple\Container;
/**
* Class BaseFormatter
@@ -23,11 +23,24 @@ abstract class BaseFormatter extends Base
protected $query;
/**
+ * Get object instance
+ *
+ * @static
+ * @access public
+ * @param Container $container
+ * @return static
+ */
+ public static function getInstance(Container $container)
+ {
+ return new static($container);
+ }
+
+ /**
* Set query
*
* @access public
* @param Table $query
- * @return FormatterInterface
+ * @return $this
*/
public function withQuery(Table $query)
{
diff --git a/app/Formatter/BoardColumnFormatter.php b/app/Formatter/BoardColumnFormatter.php
new file mode 100644
index 00000000..3d8f6e67
--- /dev/null
+++ b/app/Formatter/BoardColumnFormatter.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+use Kanboard\Core\Filter\FormatterInterface;
+
+/**
+ * Board Column Formatter
+ *
+ * @package formatter
+ * @author Frederic Guillot
+ */
+class BoardColumnFormatter extends BaseFormatter implements FormatterInterface
+{
+ protected $swimlaneId = 0;
+ protected $columns = array();
+ protected $tasks = array();
+
+ /**
+ * Set swimlaneId
+ *
+ * @access public
+ * @param integer $swimlaneId
+ * @return $this
+ */
+ public function withSwimlaneId($swimlaneId)
+ {
+ $this->swimlaneId = $swimlaneId;
+ return $this;
+ }
+
+ /**
+ * Set columns
+ *
+ * @access public
+ * @param array $columns
+ * @return $this
+ */
+ public function withColumns(array $columns)
+ {
+ $this->columns = $columns;
+ return $this;
+ }
+
+ /**
+ * Set tasks
+ *
+ * @access public
+ * @param array $tasks
+ * @return $this
+ */
+ public function withTasks(array $tasks)
+ {
+ $this->tasks = $tasks;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return array
+ */
+ public function format()
+ {
+ foreach ($this->columns as &$column) {
+ $column['tasks'] = BoardTaskFormatter::getInstance($this->container)
+ ->withTasks($this->tasks)
+ ->withSwimlaneId($this->swimlaneId)
+ ->withColumnId($column['id'])
+ ->format();
+
+ $column['nb_tasks'] = count($column['tasks']);
+ $column['score'] = (int) array_column_sum($column['tasks'], 'score');
+ }
+
+ return $this->columns;
+ }
+}
diff --git a/app/Formatter/BoardFormatter.php b/app/Formatter/BoardFormatter.php
index dbc7cf21..562a97bc 100644
--- a/app/Formatter/BoardFormatter.php
+++ b/app/Formatter/BoardFormatter.php
@@ -28,7 +28,7 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface
* @param integer $projectId
* @return $this
*/
- public function setProjectId($projectId)
+ public function withProjectId($projectId)
{
$this->projectId = $projectId;
return $this;
@@ -42,15 +42,22 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface
*/
public function format()
{
+ $swimlanes = $this->swimlaneModel->getSwimlanes($this->projectId);
+ $columns = $this->columnModel->getAll($this->projectId);
+
$tasks = $this->query
->eq(TaskModel::TABLE.'.project_id', $this->projectId)
->asc(TaskModel::TABLE.'.position')
->findAll();
- return $this->boardModel->getBoard($this->projectId, function ($project_id, $column_id, $swimlane_id) use ($tasks) {
- return array_filter($tasks, function (array $task) use ($column_id, $swimlane_id) {
- return $task['column_id'] == $column_id && $task['swimlane_id'] == $swimlane_id;
- });
- });
+ if (empty($swimlanes) || empty($columns)) {
+ return array();
+ }
+
+ return BoardSwimlaneFormatter::getInstance($this->container)
+ ->withSwimlanes($swimlanes)
+ ->withColumns($columns)
+ ->withTasks($tasks)
+ ->format();
}
}
diff --git a/app/Formatter/BoardSwimlaneFormatter.php b/app/Formatter/BoardSwimlaneFormatter.php
new file mode 100644
index 00000000..91b4bfd7
--- /dev/null
+++ b/app/Formatter/BoardSwimlaneFormatter.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+use Kanboard\Core\Filter\FormatterInterface;
+
+/**
+ * Board Swimlane Formatter
+ *
+ * @package formatter
+ * @author Frederic Guillot
+ */
+class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface
+{
+ protected $swimlanes = array();
+ protected $columns = array();
+ protected $tasks = array();
+
+ /**
+ * Set swimlanes
+ *
+ * @access public
+ * @param array $swimlanes
+ * @return $this
+ */
+ public function withSwimlanes($swimlanes)
+ {
+ $this->swimlanes = $swimlanes;
+ return $this;
+ }
+
+ /**
+ * Set columns
+ *
+ * @access public
+ * @param array $columns
+ * @return $this
+ */
+ public function withColumns($columns)
+ {
+ $this->columns = $columns;
+ return $this;
+ }
+
+ /**
+ * Set tasks
+ *
+ * @access public
+ * @param array $tasks
+ * @return $this
+ */
+ public function withTasks(array $tasks)
+ {
+ $this->tasks = $tasks;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return array
+ */
+ public function format()
+ {
+ $nb_swimlanes = count($this->swimlanes);
+ $nb_columns = count($this->columns);
+
+ foreach ($this->swimlanes as &$swimlane) {
+ $swimlane['columns'] = BoardColumnFormatter::getInstance($this->container)
+ ->withSwimlaneId($swimlane['id'])
+ ->withColumns($this->columns)
+ ->withTasks($this->tasks)
+ ->format();
+
+ $swimlane['nb_swimlanes'] = $nb_swimlanes;
+ $swimlane['nb_columns'] = $nb_columns;
+ $swimlane['nb_tasks'] = array_column_sum($swimlane['columns'], 'nb_tasks');
+ $swimlane['score'] = array_column_sum($swimlane['columns'], 'score');
+
+ $this->calculateStatsByColumnAcrossSwimlanes($swimlane['columns']);
+ }
+
+ return $this->swimlanes;
+ }
+
+ /**
+ * Calculate stats for each column acrosss all swimlanes
+ *
+ * @access protected
+ * @param array $columns
+ */
+ protected function calculateStatsByColumnAcrossSwimlanes(array $columns)
+ {
+ foreach ($columns as $columnIndex => $column) {
+ if (! isset($this->swimlanes[0]['columns'][$columnIndex]['column_nb_tasks'])) {
+ $this->swimlanes[0]['columns'][$columnIndex]['column_nb_tasks'] = 0;
+ $this->swimlanes[0]['columns'][$columnIndex]['column_score'] = 0;
+ }
+
+ $this->swimlanes[0]['columns'][$columnIndex]['column_nb_tasks'] += $column['nb_tasks'];
+ $this->swimlanes[0]['columns'][$columnIndex]['column_score'] += $column['score'];
+ }
+ }
+}
diff --git a/app/Formatter/BoardTaskFormatter.php b/app/Formatter/BoardTaskFormatter.php
new file mode 100644
index 00000000..d9500710
--- /dev/null
+++ b/app/Formatter/BoardTaskFormatter.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+use Kanboard\Core\Filter\FormatterInterface;
+
+/**
+ * Board Task Formatter
+ *
+ * @package formatter
+ * @author Frederic Guillot
+ */
+class BoardTaskFormatter extends BaseFormatter implements FormatterInterface
+{
+ protected $tasks = array();
+ protected $columnId = 0;
+ protected $swimlaneId = 0;
+
+ /**
+ * Set tasks
+ *
+ * @access public
+ * @param array $tasks
+ * @return $this
+ */
+ public function withTasks(array $tasks)
+ {
+ $this->tasks = $tasks;
+ return $this;
+ }
+
+ /**
+ * Set columnId
+ *
+ * @access public
+ * @param integer $columnId
+ * @return $this
+ */
+ public function withColumnId($columnId)
+ {
+ $this->columnId = $columnId;
+ return $this;
+ }
+
+ /**
+ * Set swimlaneId
+ *
+ * @access public
+ * @param integer $swimlaneId
+ * @return $this
+ */
+ public function withSwimlaneId($swimlaneId)
+ {
+ $this->swimlaneId = $swimlaneId;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return array
+ */
+ public function format()
+ {
+ return array_values(array_filter($this->tasks, array($this, 'filterTasks')));
+ }
+
+ /**
+ * Keep only tasks of the given column and swimlane
+ *
+ * @access public
+ * @param array $task
+ * @return bool
+ */
+ public function filterTasks(array $task)
+ {
+ return $task['column_id'] == $this->columnId && $task['swimlane_id'] == $this->swimlaneId;
+ }
+}
diff --git a/app/Model/BoardModel.php b/app/Model/BoardModel.php
index d2718b47..4d559936 100644
--- a/app/Model/BoardModel.php
+++ b/app/Model/BoardModel.php
@@ -94,66 +94,6 @@ class BoardModel extends Base
}
/**
- * Get all tasks sorted by columns and swimlanes
- *
- * @access public
- * @param integer $project_id
- * @param callable $callback
- * @return array
- */
- public function getBoard($project_id, $callback = null)
- {
- $swimlanes = $this->swimlaneModel->getSwimlanes($project_id);
- $columns = $this->columnModel->getAll($project_id);
- $nb_columns = count($columns);
-
- for ($i = 0, $ilen = count($swimlanes); $i < $ilen; $i++) {
- $swimlanes[$i]['columns'] = $columns;
- $swimlanes[$i]['nb_columns'] = $nb_columns;
- $swimlanes[$i]['nb_tasks'] = 0;
- $swimlanes[$i]['nb_swimlanes'] = $ilen;
-
- for ($j = 0; $j < $nb_columns; $j++) {
- $column_id = $columns[$j]['id'];
- $swimlane_id = $swimlanes[$i]['id'];
-
- if (! isset($swimlanes[0]['columns'][$j]['nb_column_tasks'])) {
- $swimlanes[0]['columns'][$j]['nb_column_tasks'] = 0;
- $swimlanes[0]['columns'][$j]['total_score'] = 0;
- }
-
- $swimlanes[$i]['columns'][$j]['tasks'] = $callback === null ? $this->taskFinderModel->getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id) : $callback($project_id, $column_id, $swimlane_id);
- $swimlanes[$i]['columns'][$j]['nb_tasks'] = count($swimlanes[$i]['columns'][$j]['tasks']);
- $swimlanes[$i]['columns'][$j]['score'] = $this->getColumnSum($swimlanes[$i]['columns'][$j]['tasks'], 'score');
- $swimlanes[$i]['nb_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks'];
- $swimlanes[0]['columns'][$j]['nb_column_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks'];
- $swimlanes[0]['columns'][$j]['total_score'] += $swimlanes[$i]['columns'][$j]['score'];
- }
- }
-
- return $swimlanes;
- }
-
- /**
- * Calculate the sum of the defined field for a list of tasks
- *
- * @access public
- * @param array $tasks
- * @param string $field
- * @return integer
- */
- public function getColumnSum(array &$tasks, $field)
- {
- $sum = 0;
-
- foreach ($tasks as $task) {
- $sum += $task[$field];
- }
-
- return $sum;
- }
-
- /**
* Get the total of tasks per column
*
* @access public
diff --git a/app/Model/TaskFinderModel.php b/app/Model/TaskFinderModel.php
index 8b636e28..0e99c407 100644
--- a/app/Model/TaskFinderModel.php
+++ b/app/Model/TaskFinderModel.php
@@ -153,26 +153,6 @@ class TaskFinderModel extends Base
}
/**
- * Get all tasks shown on the board (sorted by position)
- *
- * @access public
- * @param integer $project_id Project id
- * @param integer $column_id Column id
- * @param integer $swimlane_id Swimlane id
- * @return array
- */
- public function getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id = 0)
- {
- return $this->getExtendedQuery()
- ->eq(TaskModel::TABLE.'.project_id', $project_id)
- ->eq(TaskModel::TABLE.'.column_id', $column_id)
- ->eq(TaskModel::TABLE.'.swimlane_id', $swimlane_id)
- ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN)
- ->asc(TaskModel::TABLE.'.position')
- ->findAll();
- }
-
- /**
* Get all tasks for a given project and status
*
* @access public
diff --git a/app/Template/board/table_column.php b/app/Template/board/table_column.php
index f7a9f6ad..6336234a 100644
--- a/app/Template/board/table_column.php
+++ b/app/Template/board/table_column.php
@@ -18,9 +18,9 @@
</div>
<?php endif ?>
- <?php if ($swimlane['nb_swimlanes'] > 1 && ! empty($column['nb_column_tasks'])): ?>
+ <?php if ($swimlane['nb_swimlanes'] > 1 && ! empty($column['column_nb_tasks'])): ?>
<span title="<?= t('Total number of tasks in this column across all swimlanes') ?>" class="board-column-header-task-count">
- (<span><?= $column['nb_column_tasks'] ?></span>)
+ (<span><?= $column['column_nb_tasks'] ?></span>)
</span>
<?php endif ?>
diff --git a/app/functions.php b/app/functions.php
index b759763f..99431d9e 100644
--- a/app/functions.php
+++ b/app/functions.php
@@ -3,6 +3,32 @@
use Kanboard\Core\Translator;
/**
+ * Sum all values from a single column in the input array
+ *
+ * $input = [
+ * ['column' => 2'], ['column' => 3']
+ * ]
+ *
+ * array_column_sum($input, 'column') returns 5
+ *
+ * @param array $input
+ * @param string $column
+ * @return double
+ */
+function array_column_sum(array &$input, $column)
+{
+ $sum = 0.0;
+
+ foreach ($input as &$row) {
+ if (isset($row[$column])) {
+ $sum += (float) $row[$column];
+ }
+ }
+
+ return $sum;
+}
+
+/**
* Build version number from git-archive output
*
* @param string $ref
diff --git a/tests/units/Formatter/BoardFormatterTest.php b/tests/units/Formatter/BoardFormatterTest.php
new file mode 100644
index 00000000..02b0b518
--- /dev/null
+++ b/tests/units/Formatter/BoardFormatterTest.php
@@ -0,0 +1,311 @@
+<?php
+
+use Kanboard\Formatter\BoardFormatter;
+use Kanboard\Model\ColumnModel;
+use Kanboard\Model\ProjectModel;
+use Kanboard\Model\SwimlaneModel;
+use Kanboard\Model\TaskCreationModel;
+use Kanboard\Model\TaskFinderModel;
+
+require_once __DIR__.'/../Base.php';
+
+class BoardFormatterTest extends Base
+{
+ public function testFormatWithSwimlanes()
+ {
+ $projectModel = new ProjectModel($this->container);
+ $swimlaneModel = new SwimlaneModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
+ $this->assertEquals(1, $swimlaneModel->create(array('name' => 'Swimlane 1', 'project_id' => 1)));
+ $this->assertEquals(2, $swimlaneModel->create(array('name' => 'Swimlane 2', 'project_id' => 1)));
+
+ // 2 task within the same column but no score
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task 1', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'Task 2', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 1)));
+
+ // 2 tasks in the same column with score
+ $this->assertEquals(3, $taskCreationModel->create(array('title' => 'Task 3', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 1, 'score' => 4)));
+ $this->assertEquals(4, $taskCreationModel->create(array('title' => 'Task 4', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 1, 'score' => 5)));
+
+ // 1 task in 2nd column
+ $this->assertEquals(5, $taskCreationModel->create(array('title' => 'Task 5', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 2)));
+
+ // tasks in same column but different swimlanes
+ $this->assertEquals(6, $taskCreationModel->create(array('title' => 'Task 6', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 3, 'score' => 1)));
+ $this->assertEquals(7, $taskCreationModel->create(array('title' => 'Task 7', 'project_id' => 1, 'swimlane_id' => 1, 'column_id' => 3, 'score' => 2)));
+ $this->assertEquals(8, $taskCreationModel->create(array('title' => 'Task 8', 'project_id' => 1, 'swimlane_id' => 2, 'column_id' => 3, 'score' => 3)));
+
+ $board = BoardFormatter::getInstance($this->container)
+ ->withQuery($taskFinderModel->getExtendedQuery())
+ ->withProjectId(1)
+ ->format();
+
+ $this->assertCount(3, $board);
+
+ $this->assertEquals('Default swimlane', $board[0]['name']);
+ $this->assertCount(4, $board[0]['columns']);
+ $this->assertEquals(3, $board[0]['nb_swimlanes']);
+ $this->assertEquals(4, $board[0]['nb_columns']);
+ $this->assertEquals(6, $board[0]['nb_tasks']);
+ $this->assertEquals(10, $board[0]['score']);
+
+ $this->assertEquals(4, $board[0]['columns'][0]['column_nb_tasks']);
+ $this->assertEquals(1, $board[0]['columns'][1]['column_nb_tasks']);
+ $this->assertEquals(3, $board[0]['columns'][2]['column_nb_tasks']);
+ $this->assertEquals(0, $board[0]['columns'][3]['column_nb_tasks']);
+
+ $this->assertEquals(9, $board[0]['columns'][0]['column_score']);
+ $this->assertEquals(0, $board[0]['columns'][1]['column_score']);
+ $this->assertEquals(6, $board[0]['columns'][2]['column_score']);
+ $this->assertEquals(0, $board[0]['columns'][3]['column_score']);
+
+ $this->assertSame(9, $board[0]['columns'][0]['score']);
+ $this->assertSame(0, $board[0]['columns'][1]['score']);
+ $this->assertSame(1, $board[0]['columns'][2]['score']);
+ $this->assertSame(0, $board[0]['columns'][3]['score']);
+
+ $this->assertSame(4, $board[0]['columns'][0]['nb_tasks']);
+ $this->assertSame(1, $board[0]['columns'][1]['nb_tasks']);
+ $this->assertSame(1, $board[0]['columns'][2]['nb_tasks']);
+ $this->assertSame(0, $board[0]['columns'][3]['nb_tasks']);
+
+ $this->assertEquals('Task 1', $board[0]['columns'][0]['tasks'][0]['title']);
+ $this->assertEquals('Task 2', $board[0]['columns'][0]['tasks'][1]['title']);
+ $this->assertEquals('Task 3', $board[0]['columns'][0]['tasks'][2]['title']);
+ $this->assertEquals('Task 4', $board[0]['columns'][0]['tasks'][3]['title']);
+ $this->assertEquals('Task 5', $board[0]['columns'][1]['tasks'][0]['title']);
+ $this->assertEquals('Task 6', $board[0]['columns'][2]['tasks'][0]['title']);
+
+ $this->assertEquals('Swimlane 1', $board[1]['name']);
+ $this->assertCount(4, $board[1]['columns']);
+ $this->assertEquals(3, $board[1]['nb_swimlanes']);
+ $this->assertEquals(4, $board[1]['nb_columns']);
+ $this->assertEquals(1, $board[1]['nb_tasks']);
+ $this->assertEquals(2, $board[1]['score']);
+
+ $this->assertSame(0, $board[1]['columns'][0]['score']);
+ $this->assertSame(0, $board[1]['columns'][1]['score']);
+ $this->assertSame(2, $board[1]['columns'][2]['score']);
+ $this->assertSame(0, $board[1]['columns'][3]['score']);
+
+ $this->assertSame(0, $board[1]['columns'][0]['nb_tasks']);
+ $this->assertSame(0, $board[1]['columns'][1]['nb_tasks']);
+ $this->assertSame(1, $board[1]['columns'][2]['nb_tasks']);
+ $this->assertSame(0, $board[1]['columns'][3]['nb_tasks']);
+
+ $this->assertEquals('Task 7', $board[1]['columns'][2]['tasks'][0]['title']);
+
+ $this->assertEquals('Swimlane 2', $board[2]['name']);
+ $this->assertCount(4, $board[2]['columns']);
+ $this->assertEquals(3, $board[2]['nb_swimlanes']);
+ $this->assertEquals(4, $board[2]['nb_columns']);
+ $this->assertEquals(1, $board[2]['nb_tasks']);
+ $this->assertEquals(3, $board[2]['score']);
+
+ $this->assertSame(0, $board[2]['columns'][0]['score']);
+ $this->assertSame(0, $board[2]['columns'][1]['score']);
+ $this->assertSame(3, $board[2]['columns'][2]['score']);
+ $this->assertSame(0, $board[2]['columns'][3]['score']);
+
+ $this->assertSame(0, $board[2]['columns'][0]['nb_tasks']);
+ $this->assertSame(0, $board[2]['columns'][1]['nb_tasks']);
+ $this->assertSame(1, $board[2]['columns'][2]['nb_tasks']);
+ $this->assertSame(0, $board[2]['columns'][3]['nb_tasks']);
+
+ $this->assertEquals('Task 8', $board[2]['columns'][2]['tasks'][0]['title']);
+ }
+
+ public function testFormatWithoutDefaultSwimlane()
+ {
+ $projectModel = new ProjectModel($this->container);
+ $swimlaneModel = new SwimlaneModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
+ $this->assertTrue($swimlaneModel->disableDefault(1));
+ $this->assertEquals(1, $swimlaneModel->create(array('name' => 'Swimlane 1', 'project_id' => 1)));
+ $this->assertEquals(2, $swimlaneModel->create(array('name' => 'Swimlane 2', 'project_id' => 1)));
+
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task 1', 'project_id' => 1, 'swimlane_id' => 1, 'column_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'Task 2', 'project_id' => 1, 'swimlane_id' => 2, 'column_id' => 2)));
+ $this->assertEquals(3, $taskCreationModel->create(array('title' => 'Task 3', 'project_id' => 1, 'swimlane_id' => 1, 'column_id' => 2, 'score' => 1)));
+ $this->assertEquals(4, $taskCreationModel->create(array('title' => 'Task 4', 'project_id' => 1, 'swimlane_id' => 2, 'column_id' => 1)));
+
+ $board = BoardFormatter::getInstance($this->container)
+ ->withQuery($taskFinderModel->getExtendedQuery())
+ ->withProjectId(1)
+ ->format();
+
+ $this->assertCount(2, $board);
+
+ $this->assertEquals('Swimlane 1', $board[0]['name']);
+ $this->assertCount(4, $board[0]['columns']);
+ $this->assertEquals(2, $board[0]['nb_swimlanes']);
+ $this->assertEquals(4, $board[0]['nb_columns']);
+ $this->assertEquals(2, $board[0]['nb_tasks']);
+ $this->assertEquals(1, $board[0]['score']);
+
+ $this->assertEquals(2, $board[0]['columns'][0]['column_nb_tasks']);
+ $this->assertEquals(2, $board[0]['columns'][1]['column_nb_tasks']);
+ $this->assertEquals(0, $board[0]['columns'][2]['column_nb_tasks']);
+ $this->assertEquals(0, $board[0]['columns'][3]['column_nb_tasks']);
+
+ $this->assertEquals(0, $board[0]['columns'][0]['column_score']);
+ $this->assertEquals(1, $board[0]['columns'][1]['column_score']);
+ $this->assertEquals(0, $board[0]['columns'][2]['column_score']);
+ $this->assertEquals(0, $board[0]['columns'][3]['column_score']);
+
+ $this->assertSame(0, $board[0]['columns'][0]['score']);
+ $this->assertSame(1, $board[0]['columns'][1]['score']);
+ $this->assertSame(0, $board[0]['columns'][2]['score']);
+ $this->assertSame(0, $board[0]['columns'][3]['score']);
+
+ $this->assertSame(1, $board[0]['columns'][0]['nb_tasks']);
+ $this->assertSame(1, $board[0]['columns'][1]['nb_tasks']);
+ $this->assertSame(0, $board[0]['columns'][2]['nb_tasks']);
+ $this->assertSame(0, $board[0]['columns'][3]['nb_tasks']);
+
+ $this->assertEquals('Task 1', $board[0]['columns'][0]['tasks'][0]['title']);
+ $this->assertEquals('Task 3', $board[0]['columns'][1]['tasks'][0]['title']);
+
+ $this->assertEquals('Swimlane 2', $board[1]['name']);
+ $this->assertCount(4, $board[1]['columns']);
+ $this->assertEquals(2, $board[1]['nb_swimlanes']);
+ $this->assertEquals(4, $board[1]['nb_columns']);
+ $this->assertEquals(2, $board[1]['nb_tasks']);
+ $this->assertEquals(0, $board[1]['score']);
+
+ $this->assertSame(0, $board[1]['columns'][0]['score']);
+ $this->assertSame(0, $board[1]['columns'][1]['score']);
+ $this->assertSame(0, $board[1]['columns'][2]['score']);
+ $this->assertSame(0, $board[1]['columns'][3]['score']);
+
+ $this->assertSame(1, $board[1]['columns'][0]['nb_tasks']);
+ $this->assertSame(1, $board[1]['columns'][1]['nb_tasks']);
+ $this->assertSame(0, $board[1]['columns'][2]['nb_tasks']);
+ $this->assertSame(0, $board[1]['columns'][3]['nb_tasks']);
+
+ $this->assertEquals('Task 4', $board[1]['columns'][0]['tasks'][0]['title']);
+ $this->assertEquals('Task 2', $board[1]['columns'][1]['tasks'][0]['title']);
+ }
+
+ public function testFormatWithoutSwimlane()
+ {
+ $projectModel = new ProjectModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+ $swimlaneModel = new SwimlaneModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
+ $this->assertTrue($swimlaneModel->disableDefault(1));
+
+ $board = BoardFormatter::getInstance($this->container)
+ ->withQuery($taskFinderModel->getExtendedQuery())
+ ->withProjectId(1)
+ ->format();
+
+ $this->assertCount(0, $board);
+ }
+
+ public function testFormatWithoutColumn()
+ {
+ $projectModel = new ProjectModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+ $columnModel = new ColumnModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
+ $this->assertTrue($columnModel->remove(1));
+ $this->assertTrue($columnModel->remove(2));
+ $this->assertTrue($columnModel->remove(3));
+ $this->assertTrue($columnModel->remove(4));
+
+ $board = BoardFormatter::getInstance($this->container)
+ ->withQuery($taskFinderModel->getExtendedQuery())
+ ->withProjectId(1)
+ ->format();
+
+ $this->assertCount(0, $board);
+ }
+
+ public function testFormatWithoutTask()
+ {
+ $projectModel = new ProjectModel($this->container);
+ $swimlaneModel = new SwimlaneModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
+ $this->assertEquals(1, $swimlaneModel->create(array('name' => 'Swimlane 1', 'project_id' => 1)));
+ $this->assertEquals(2, $swimlaneModel->create(array('name' => 'Swimlane 2', 'project_id' => 1)));
+
+ $board = BoardFormatter::getInstance($this->container)
+ ->withQuery($taskFinderModel->getExtendedQuery())
+ ->withProjectId(1)
+ ->format();
+
+ $this->assertCount(3, $board);
+
+ $this->assertEquals('Default swimlane', $board[0]['name']);
+ $this->assertCount(4, $board[0]['columns']);
+ $this->assertEquals(3, $board[0]['nb_swimlanes']);
+ $this->assertEquals(4, $board[0]['nb_columns']);
+ $this->assertEquals(0, $board[0]['nb_tasks']);
+ $this->assertEquals(0, $board[0]['score']);
+
+ $this->assertEquals(0, $board[0]['columns'][0]['column_nb_tasks']);
+ $this->assertEquals(0, $board[0]['columns'][1]['column_nb_tasks']);
+ $this->assertEquals(0, $board[0]['columns'][2]['column_nb_tasks']);
+ $this->assertEquals(0, $board[0]['columns'][3]['column_nb_tasks']);
+
+ $this->assertEquals(0, $board[0]['columns'][0]['column_score']);
+ $this->assertEquals(0, $board[0]['columns'][1]['column_score']);
+ $this->assertEquals(0, $board[0]['columns'][2]['column_score']);
+ $this->assertEquals(0, $board[0]['columns'][3]['column_score']);
+
+ $this->assertSame(0, $board[0]['columns'][0]['score']);
+ $this->assertSame(0, $board[0]['columns'][1]['score']);
+ $this->assertSame(0, $board[0]['columns'][2]['score']);
+ $this->assertSame(0, $board[0]['columns'][3]['score']);
+
+ $this->assertSame(0, $board[0]['columns'][0]['nb_tasks']);
+ $this->assertSame(0, $board[0]['columns'][1]['nb_tasks']);
+ $this->assertSame(0, $board[0]['columns'][2]['nb_tasks']);
+ $this->assertSame(0, $board[0]['columns'][3]['nb_tasks']);
+
+ $this->assertEquals('Swimlane 1', $board[1]['name']);
+ $this->assertCount(4, $board[1]['columns']);
+ $this->assertEquals(3, $board[1]['nb_swimlanes']);
+ $this->assertEquals(4, $board[1]['nb_columns']);
+ $this->assertEquals(0, $board[1]['nb_tasks']);
+ $this->assertEquals(0, $board[1]['score']);
+
+ $this->assertSame(0, $board[1]['columns'][0]['score']);
+ $this->assertSame(0, $board[1]['columns'][1]['score']);
+ $this->assertSame(0, $board[1]['columns'][2]['score']);
+ $this->assertSame(0, $board[1]['columns'][3]['score']);
+
+ $this->assertSame(0, $board[1]['columns'][0]['nb_tasks']);
+ $this->assertSame(0, $board[1]['columns'][1]['nb_tasks']);
+ $this->assertSame(0, $board[1]['columns'][2]['nb_tasks']);
+ $this->assertSame(0, $board[1]['columns'][3]['nb_tasks']);
+
+ $this->assertEquals('Swimlane 2', $board[2]['name']);
+ $this->assertCount(4, $board[2]['columns']);
+ $this->assertEquals(3, $board[2]['nb_swimlanes']);
+ $this->assertEquals(4, $board[2]['nb_columns']);
+ $this->assertEquals(0, $board[2]['nb_tasks']);
+ $this->assertEquals(0, $board[2]['score']);
+
+ $this->assertSame(0, $board[2]['columns'][0]['score']);
+ $this->assertSame(0, $board[2]['columns'][1]['score']);
+ $this->assertSame(0, $board[2]['columns'][2]['score']);
+ $this->assertSame(0, $board[2]['columns'][3]['score']);
+
+ $this->assertSame(0, $board[2]['columns'][0]['nb_tasks']);
+ $this->assertSame(0, $board[2]['columns'][1]['nb_tasks']);
+ $this->assertSame(0, $board[2]['columns'][2]['nb_tasks']);
+ $this->assertSame(0, $board[2]['columns'][3]['nb_tasks']);
+ }
+}
diff --git a/tests/units/FunctionTest.php b/tests/units/FunctionTest.php
new file mode 100644
index 00000000..72895845
--- /dev/null
+++ b/tests/units/FunctionTest.php
@@ -0,0 +1,21 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+class FunctionTest extends Base
+{
+ public function testArrayColumnSum()
+ {
+ $input = array(
+ array(
+ 'my_column' => 123
+ ),
+ array(
+ 'my_column' => 456.7
+ ),
+ array()
+ );
+
+ $this->assertSame(579.7, array_column_sum($input, 'my_column'));
+ }
+}
diff --git a/tests/units/Model/BoardTest.php b/tests/units/Model/BoardTest.php
index 80587d89..9f540c63 100644
--- a/tests/units/Model/BoardTest.php
+++ b/tests/units/Model/BoardTest.php
@@ -3,12 +3,8 @@
require_once __DIR__.'/../Base.php';
use Kanboard\Model\ProjectModel;
-use Kanboard\Model\BoardModel;
use Kanboard\Model\ColumnModel;
use Kanboard\Model\ConfigModel;
-use Kanboard\Model\TaskCreationModel;
-use Kanboard\Model\TaskFinderModel;
-use Kanboard\Model\SwimlaneModel;
class BoardTest extends Base
{
@@ -45,121 +41,4 @@ class BoardTest extends Base
$this->assertEquals('column #1', $columns[5]);
$this->assertEquals('column #2', $columns[6]);
}
-
- public function testGetBoard()
- {
- $p = new ProjectModel($this->container);
- $b = new BoardModel($this->container);
-
- $this->assertEquals(1, $p->create(array('name' => 'UnitTest1')));
-
- $board = $b->getBoard(1);
- $this->assertNotEmpty($board);
- $this->assertEquals(1, count($board));
- $this->assertEquals(6, count($board[0]));
- $this->assertArrayHasKey('name', $board[0]);
- $this->assertArrayHasKey('nb_tasks', $board[0]);
- $this->assertArrayHasKey('columns', $board[0]);
- $this->assertArrayHasKey('tasks', $board[0]['columns'][2]);
- $this->assertArrayHasKey('nb_tasks', $board[0]['columns'][2]);
- $this->assertArrayHasKey('title', $board[0]['columns'][2]);
- $this->assertArrayHasKey('nb_column_tasks', $board[0]['columns'][0]);
- $this->assertArrayHasKey('total_score', $board[0]['columns'][0]);
- }
-
- public function testGetBoardWithSwimlane()
- {
- $b = new BoardModel($this->container);
- $tc = new TaskCreationModel($this->container);
- $tf = new TaskFinderModel($this->container);
- $p = new ProjectModel($this->container);
- $s = new SwimlaneModel($this->container);
-
- $this->assertEquals(1, $p->create(array('name' => 'Project #1')));
- $this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'test 1')));
- $this->assertEquals(1, $tc->create(array('title' => 'Task #1', 'project_id' => 1, 'column_id' => 1)));
- $this->assertEquals(2, $tc->create(array('title' => 'Task #2', 'project_id' => 1, 'column_id' => 3)));
- $this->assertEquals(3, $tc->create(array('title' => 'Task #3', 'project_id' => 1, 'column_id' => 2, 'swimlane_id' => 1)));
- $this->assertEquals(4, $tc->create(array('title' => 'Task #4', 'project_id' => 1, 'column_id' => 3)));
- $this->assertEquals(5, $tc->create(array('title' => 'Task #5', 'project_id' => 1, 'column_id' => 4, 'score' => 2)));
- $this->assertEquals(6, $tc->create(array('title' => 'Task #6', 'project_id' => 1, 'column_id' => 4, 'score' => 3, 'swimlane_id' => 1)));
-
- $board = $b->getBoard(1);
- $this->assertNotEmpty($board);
- $this->assertEquals(2, count($board));
- $this->assertEquals(6, count($board[0]));
- $this->assertArrayHasKey('name', $board[0]);
- $this->assertArrayHasKey('nb_tasks', $board[0]);
- $this->assertArrayHasKey('columns', $board[0]);
- $this->assertArrayHasKey('tasks', $board[0]['columns'][2]);
- $this->assertArrayHasKey('nb_tasks', $board[0]['columns'][2]);
- $this->assertArrayHasKey('title', $board[0]['columns'][2]);
- $this->assertArrayHasKey('nb_column_tasks', $board[0]['columns'][0]);
- $this->assertArrayNotHasKey('nb_column_tasks', $board[1]['columns'][0]);
- $this->assertArrayNotHasKey('total_score', $board[1]['columns'][0]);
- $this->assertArrayHasKey('score', $board[0]['columns'][3]);
- $this->assertArrayHasKey('total_score', $board[0]['columns'][3]);
- $this->assertEquals(2, $board[0]['columns'][3]['score']);
- $this->assertEquals(5, $board[0]['columns'][3]['total_score']);
-
- $task = $tf->getById(1);
- $this->assertEquals(1, $task['id']);
- $this->assertEquals(1, $task['column_id']);
- $this->assertEquals(1, $task['position']);
- $this->assertEquals(0, $task['swimlane_id']);
- $this->assertEquals(1, $board[0]['columns'][0]['tasks'][0]['id']);
- $this->assertEquals(1, $board[0]['columns'][0]['tasks'][0]['column_id']);
- $this->assertEquals(1, $board[0]['columns'][0]['tasks'][0]['position']);
- $this->assertEquals(0, $board[0]['columns'][0]['tasks'][0]['swimlane_id']);
-
- $task = $tf->getById(2);
- $this->assertEquals(2, $task['id']);
- $this->assertEquals(3, $task['column_id']);
- $this->assertEquals(1, $task['position']);
- $this->assertEquals(0, $task['swimlane_id']);
- $this->assertEquals(2, $board[0]['columns'][2]['tasks'][0]['id']);
- $this->assertEquals(3, $board[0]['columns'][2]['tasks'][0]['column_id']);
- $this->assertEquals(1, $board[0]['columns'][2]['tasks'][0]['position']);
- $this->assertEquals(0, $board[0]['columns'][2]['tasks'][0]['swimlane_id']);
-
- $task = $tf->getById(3);
- $this->assertEquals(3, $task['id']);
- $this->assertEquals(2, $task['column_id']);
- $this->assertEquals(1, $task['position']);
- $this->assertEquals(1, $task['swimlane_id']);
- $this->assertEquals(3, $board[1]['columns'][1]['tasks'][0]['id']);
- $this->assertEquals(2, $board[1]['columns'][1]['tasks'][0]['column_id']);
- $this->assertEquals(1, $board[1]['columns'][1]['tasks'][0]['position']);
- $this->assertEquals(1, $board[1]['columns'][1]['tasks'][0]['swimlane_id']);
-
- $task = $tf->getById(4);
- $this->assertEquals(4, $task['id']);
- $this->assertEquals(3, $task['column_id']);
- $this->assertEquals(2, $task['position']);
- $this->assertEquals(0, $task['swimlane_id']);
- $this->assertEquals(4, $board[0]['columns'][2]['tasks'][1]['id']);
- $this->assertEquals(3, $board[0]['columns'][2]['tasks'][1]['column_id']);
- $this->assertEquals(2, $board[0]['columns'][2]['tasks'][1]['position']);
- $this->assertEquals(0, $board[0]['columns'][2]['tasks'][1]['swimlane_id']);
-
- $task = $tf->getById(5);
- $this->assertEquals(5, $task['id']);
- $this->assertEquals(4, $task['column_id']);
- $this->assertEquals(1, $task['position']);
- $this->assertEquals(0, $task['swimlane_id']);
- $this->assertEquals(5, $board[0]['columns'][3]['tasks'][0]['id']);
- $this->assertEquals(4, $board[0]['columns'][3]['tasks'][0]['column_id']);
- $this->assertEquals(1, $board[0]['columns'][3]['tasks'][0]['position']);
- $this->assertEquals(0, $board[0]['columns'][3]['tasks'][0]['swimlane_id']);
-
- $task = $tf->getById(6);
- $this->assertEquals(6, $task['id']);
- $this->assertEquals(4, $task['column_id']);
- $this->assertEquals(1, $task['position']);
- $this->assertEquals(1, $task['swimlane_id']);
- $this->assertEquals(6, $board[1]['columns'][3]['tasks'][0]['id']);
- $this->assertEquals(4, $board[1]['columns'][3]['tasks'][0]['column_id']);
- $this->assertEquals(1, $board[1]['columns'][3]['tasks'][0]['position']);
- $this->assertEquals(1, $board[1]['columns'][3]['tasks'][0]['swimlane_id']);
- }
}