Split Task model into smaller classes

This commit is contained in:
Frédéric Guillot
2014-09-20 11:58:27 +02:00
parent 95e54d1d30
commit 5f96af82f2
16 changed files with 533 additions and 421 deletions

View File

@@ -19,14 +19,18 @@ use PicoDb\Database;
* @property \Model\Board $board
* @property \Model\Category $category
* @property \Model\Comment $comment
* @property \Model\Color $color
* @property \Model\Config $config
* @property \Model\DateParser $dateParser
* @property \Model\File $file
* @property \Model\LastLogin $lastLogin
* @property \Model\Notification $notification
* @property \Model\Project $project
* @property \Model\SubTask $subTask
* @property \Model\Task $task
* @property \Model\TaskExport $taskExport
* @property \Model\TaskHistory $taskHistory
* @property \Model\TaskValidator $taskValidator
* @property \Model\User $user
* @property \Model\Webhook $webhook
*/

31
app/Model/Color.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
namespace Model;
/**
* Color model (TODO: model for the future color picker)
*
* @package model
* @author Frederic Guillot
*/
class Color extends Base
{
/**
* Get available colors
*
* @access public
* @return array
*/
public function getList()
{
return array(
'yellow' => t('Yellow'),
'blue' => t('Blue'),
'green' => t('Green'),
'purple' => t('Purple'),
'red' => t('Red'),
'orange' => t('Orange'),
'grey' => t('Grey'),
);
}
}

85
app/Model/DateParser.php Normal file
View File

@@ -0,0 +1,85 @@
<?php
namespace Model;
use DateTime;
/**
* Date parser model
*
* @package model
* @author Frederic Guillot
*/
class DateParser extends Base
{
/**
* Return a timestamp if the given date format is correct otherwise return 0
*
* @access public
* @param string $value Date to parse
* @param string $format Date format
* @return integer
*/
public function getValidDate($value, $format)
{
$date = DateTime::createFromFormat($format, $value);
if ($date !== false) {
$errors = DateTime::getLastErrors();
if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) {
$timestamp = $date->getTimestamp();
return $timestamp > 0 ? $timestamp : 0;
}
}
return 0;
}
/**
* Parse a date ad return a unix timestamp, try different date formats
*
* @access public
* @param string $value Date to parse
* @return integer
*/
public function getTimestamp($value)
{
foreach ($this->getDateFormats() as $format) {
$timestamp = $this->getValidDate($value, $format);
if ($timestamp !== 0) {
return $timestamp;
}
}
return 0;
}
/**
* Return the list of supported date formats
*
* @access public
* @return array
*/
public function getDateFormats()
{
return array(
t('m/d/Y'),
'Y-m-d',
'Y_m_d',
);
}
/**
* For a given timestamp, reset the date to midnight
*
* @access public
* @param integer $timestamp Timestamp
* @return integer
*/
public function resetDateToMidnight($timestamp)
{
return mktime(0, 0, 0, date('m', $timestamp), date('d', $timestamp), date('Y', $timestamp));
}
}

View File

@@ -2,9 +2,6 @@
namespace Model;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
use DateTime;
use PDO;
/**
@@ -44,24 +41,7 @@ class Task extends Base
const EVENT_CREATE_UPDATE = 'task.create_update';
const EVENT_ASSIGNEE_CHANGE = 'task.assignee_change';
/**
* Get available colors
*
* @access public
* @return array
*/
public function getColors()
{
return array(
'yellow' => t('Yellow'),
'blue' => t('Blue'),
'green' => t('Green'),
'purple' => t('Purple'),
'red' => t('Red'),
'orange' => t('Orange'),
'grey' => t('Grey'),
);
}
/**
* Get a list of due tasks for all projects
@@ -372,7 +352,7 @@ class Task extends Base
}
if (! empty($values['date_due']) && ! is_numeric($values['date_due'])) {
$values['date_due'] = $this->parseDate($values['date_due']);
$values['date_due'] = $this->dateParser->getTimestamp($values['date_due']);
}
// Force integer fields at 0 (for Postgresql)
@@ -408,7 +388,7 @@ class Task extends Base
}
if (empty($values['color_id'])) {
$colors = $this->getColors();
$colors = $this->color->getList();
$values['color_id'] = key($colors);
}
@@ -708,326 +688,4 @@ class Task extends Base
return false;
}
/**
* Common validation rules
*
* @access private
* @return array
*/
private function commonValidationRules()
{
return array(
new Validators\Integer('id', t('This value must be an integer')),
new Validators\Integer('project_id', t('This value must be an integer')),
new Validators\Integer('column_id', t('This value must be an integer')),
new Validators\Integer('owner_id', t('This value must be an integer')),
new Validators\Integer('creator_id', t('This value must be an integer')),
new Validators\Integer('score', t('This value must be an integer')),
new Validators\Integer('category_id', t('This value must be an integer')),
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
new Validators\Date('date_due', t('Invalid date'), $this->getDateFormats()),
);
}
/**
* Validate task creation
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateCreation(array $values)
{
$rules = array(
new Validators\Required('project_id', t('The project is required')),
new Validators\Required('title', t('The title is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate description creation
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateDescriptionCreation(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
new Validators\Required('description', t('The description is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate task modification
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateModification(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate assignee change
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateAssigneeModification(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
new Validators\Required('project_id', t('The project is required')),
new Validators\Required('owner_id', t('This value is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate category change
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateCategoryModification(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
new Validators\Required('project_id', t('The project is required')),
new Validators\Required('category_id', t('This value is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate project modification
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateProjectModification(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
new Validators\Required('project_id', t('The project is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Return a timestamp if the given date format is correct otherwise return 0
*
* @access public
* @param string $value Date to parse
* @param string $format Date format
* @return integer
*/
public function getValidDate($value, $format)
{
$date = DateTime::createFromFormat($format, $value);
if ($date !== false) {
$errors = DateTime::getLastErrors();
if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) {
$timestamp = $date->getTimestamp();
return $timestamp > 0 ? $timestamp : 0;
}
}
return 0;
}
/**
* Parse a date ad return a unix timestamp, try different date formats
*
* @access public
* @param string $value Date to parse
* @return integer
*/
public function parseDate($value)
{
foreach ($this->getDateFormats() as $format) {
$timestamp = $this->getValidDate($value, $format);
if ($timestamp !== 0) {
return $timestamp;
}
}
return null;
}
/**
* Return the list of supported date formats
*
* @access public
* @return array
*/
public function getDateFormats()
{
return array(
t('m/d/Y'),
'Y-m-d',
'Y_m_d',
);
}
/**
* For a given timestamp, reset the date to midnight
*
* @access public
* @param integer $timestamp Timestamp
* @return integer
*/
public function resetDateToMidnight($timestamp)
{
return mktime(0, 0, 0, date('m', $timestamp), date('d', $timestamp), date('Y', $timestamp));
}
/**
* Export a list of tasks for a given project and date range
*
* @access public
* @param integer $project_id Project id
* @param mixed $from Start date (timestamp or user formatted date)
* @param mixed $to End date (timestamp or user formatted date)
* @return array
*/
public function export($project_id, $from, $to)
{
$sql = '
SELECT
tasks.id,
projects.name AS project_name,
tasks.is_active,
project_has_categories.name AS category_name,
columns.title AS column_title,
tasks.position,
tasks.color_id,
tasks.date_due,
creators.username AS creator_username,
users.username AS assignee_username,
tasks.score,
tasks.title,
tasks.date_creation,
tasks.date_modification,
tasks.date_completed
FROM tasks
LEFT JOIN users ON users.id = tasks.owner_id
LEFT JOIN users AS creators ON creators.id = tasks.creator_id
LEFT JOIN project_has_categories ON project_has_categories.id = tasks.category_id
LEFT JOIN columns ON columns.id = tasks.column_id
LEFT JOIN projects ON projects.id = tasks.project_id
WHERE tasks.date_creation >= ? AND tasks.date_creation <= ? AND tasks.project_id = ?
';
if (! is_numeric($from)) {
$from = $this->resetDateToMidnight($this->parseDate($from));
}
if (! is_numeric($to)) {
$to = $this->resetDateToMidnight(strtotime('+1 day', $this->parseDate($to)));
}
$rq = $this->db->execute($sql, array($from, $to, $project_id));
$tasks = $rq->fetchAll(PDO::FETCH_ASSOC);
$columns = array(
e('Task Id'),
e('Project'),
e('Status'),
e('Category'),
e('Column'),
e('Position'),
e('Color'),
e('Due date'),
e('Creator'),
e('Assignee'),
e('Complexity'),
e('Title'),
e('Creation date'),
e('Modification date'),
e('Completion date'),
);
$results = array($columns);
foreach ($tasks as &$task) {
$results[] = array_values($this->formatOutput($task));
}
return $results;
}
/**
* Format the output of a task array
*
* @access public
* @param array $task Task properties
* @return array
*/
public function formatOutput(array &$task)
{
$colors = $this->getColors();
$task['score'] = $task['score'] ?: '';
$task['is_active'] = $task['is_active'] == self::STATUS_OPEN ? e('Open') : e('Closed');
$task['color_id'] = $colors[$task['color_id']];
$task['date_creation'] = date('Y-m-d', $task['date_creation']);
$task['date_due'] = $task['date_due'] ? date('Y-m-d', $task['date_due']) : '';
$task['date_modification'] = $task['date_modification'] ? date('Y-m-d', $task['date_modification']) : '';
$task['date_completed'] = $task['date_completed'] ? date('Y-m-d', $task['date_completed']) : '';
return $task;
}
}

132
app/Model/TaskExport.php Normal file
View File

@@ -0,0 +1,132 @@
<?php
namespace Model;
use PDO;
/**
* Task Export model
*
* @package model
* @author Frederic Guillot
*/
class TaskExport extends Base
{
/**
* Fetch tasks and return the prepared CSV
*
* @access public
* @param integer $project_id Project id
* @param mixed $from Start date (timestamp or user formatted date)
* @param mixed $to End date (timestamp or user formatted date)
* @return array
*/
public function export($project_id, $from, $to)
{
$tasks = $this->getTasks($project_id, $from, $to);
$results = array($this->getColumns());
foreach ($tasks as &$task) {
$results[] = array_values($this->formatOutput($task));
}
return $results;
}
/**
* Get the list of tasks for a given project and date range
*
* @access public
* @param integer $project_id Project id
* @param mixed $from Start date (timestamp or user formatted date)
* @param mixed $to End date (timestamp or user formatted date)
* @return array
*/
public function getTasks($project_id, $from, $to)
{
$sql = '
SELECT
tasks.id,
projects.name AS project_name,
tasks.is_active,
project_has_categories.name AS category_name,
columns.title AS column_title,
tasks.position,
tasks.color_id,
tasks.date_due,
creators.username AS creator_username,
users.username AS assignee_username,
tasks.score,
tasks.title,
tasks.date_creation,
tasks.date_modification,
tasks.date_completed
FROM tasks
LEFT JOIN users ON users.id = tasks.owner_id
LEFT JOIN users AS creators ON creators.id = tasks.creator_id
LEFT JOIN project_has_categories ON project_has_categories.id = tasks.category_id
LEFT JOIN columns ON columns.id = tasks.column_id
LEFT JOIN projects ON projects.id = tasks.project_id
WHERE tasks.date_creation >= ? AND tasks.date_creation <= ? AND tasks.project_id = ?
';
if (! is_numeric($from)) {
$from = $this->dateParser->resetDateToMidnight($this->dateParser->getTimestamp($from));
}
if (! is_numeric($to)) {
$to = $this->dateParser->resetDateToMidnight(strtotime('+1 day', $this->dateParser->getTimestamp($to)));
}
$rq = $this->db->execute($sql, array($from, $to, $project_id));
return $rq->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Format the output of a task array
*
* @access public
* @param array $task Task properties
* @return array
*/
public function formatOutput(array &$task)
{
$colors = $this->color->getList();
$task['score'] = $task['score'] ?: '';
$task['is_active'] = $task['is_active'] == Task::STATUS_OPEN ? e('Open') : e('Closed');
$task['color_id'] = $colors[$task['color_id']];
$task['date_creation'] = date('Y-m-d', $task['date_creation']);
$task['date_due'] = $task['date_due'] ? date('Y-m-d', $task['date_due']) : '';
$task['date_modification'] = $task['date_modification'] ? date('Y-m-d', $task['date_modification']) : '';
$task['date_completed'] = $task['date_completed'] ? date('Y-m-d', $task['date_completed']) : '';
return $task;
}
/**
* Get column titles
*
* @access public
* @return array
*/
public function getColumns()
{
return array(
e('Task Id'),
e('Project'),
e('Status'),
e('Category'),
e('Column'),
e('Position'),
e('Color'),
e('Due date'),
e('Creator'),
e('Assignee'),
e('Complexity'),
e('Title'),
e('Creation date'),
e('Modification date'),
e('Completion date'),
);
}
}

170
app/Model/TaskValidator.php Normal file
View File

@@ -0,0 +1,170 @@
<?php
namespace Model;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
/**
* Task validator model
*
* @package model
* @author Frederic Guillot
*/
class TaskValidator extends Base
{
/**
* Common validation rules
*
* @access private
* @return array
*/
private function commonValidationRules()
{
return array(
new Validators\Integer('id', t('This value must be an integer')),
new Validators\Integer('project_id', t('This value must be an integer')),
new Validators\Integer('column_id', t('This value must be an integer')),
new Validators\Integer('owner_id', t('This value must be an integer')),
new Validators\Integer('creator_id', t('This value must be an integer')),
new Validators\Integer('score', t('This value must be an integer')),
new Validators\Integer('category_id', t('This value must be an integer')),
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
new Validators\Date('date_due', t('Invalid date'), $this->dateParser->getDateFormats()),
);
}
/**
* Validate task creation
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateCreation(array $values)
{
$rules = array(
new Validators\Required('project_id', t('The project is required')),
new Validators\Required('title', t('The title is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate description creation
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateDescriptionCreation(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
new Validators\Required('description', t('The description is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate task modification
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateModification(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate assignee change
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateAssigneeModification(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
new Validators\Required('project_id', t('The project is required')),
new Validators\Required('owner_id', t('This value is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate category change
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateCategoryModification(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
new Validators\Required('project_id', t('The project is required')),
new Validators\Required('category_id', t('This value is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
/**
* Validate project modification
*
* @access public
* @param array $values Form values
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateProjectModification(array $values)
{
$rules = array(
new Validators\Required('id', t('The id is required')),
new Validators\Required('project_id', t('The project is required')),
);
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
$v->getErrors()
);
}
}