Add subtasks export and move export actions to a specific controller

This commit is contained in:
Frederic Guillot
2015-01-04 21:14:57 -05:00
parent 07b07c7697
commit d1d04d6fee
27 changed files with 380 additions and 80 deletions

View File

@@ -0,0 +1,34 @@
<?php
namespace Console;
use Core\Tool;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SubtaskExport extends Base
{
protected function configure()
{
$this
->setName('export:subtasks')
->setDescription('Subtasks CSV export')
->addArgument('project_id', InputArgument::REQUIRED, 'Project id')
->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)')
->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$data = $this->subtaskExport->export(
$input->getArgument('project_id'),
$input->getArgument('start_date'),
$input->getArgument('end_date')
);
if (is_array($data)) {
Tool::csv($data);
}
}
}

75
app/Controller/Export.php Normal file
View File

@@ -0,0 +1,75 @@
<?php
namespace Controller;
/**
* Export controller
*
* @package controller
* @author Frederic Guillot
*/
class Export extends Base
{
/**
* Common export method
*
* @access private
*/
private function common($model, $method, $filename, $action, $page_title)
{
$project = $this->getProject();
$from = $this->request->getStringParam('from');
$to = $this->request->getStringParam('to');
if ($from && $to) {
$data = $this->$model->$method($project['id'], $from, $to);
$this->response->forceDownload($filename.'.csv');
$this->response->csv($data);
}
$this->response->html($this->projectLayout('export/'.$action, array(
'values' => array(
'controller' => 'export',
'action' => $action,
'project_id' => $project['id'],
'from' => $from,
'to' => $to,
),
'errors' => array(),
'date_format' => $this->config->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats(),
'project' => $project,
'title' => $page_title,
)));
}
/**
* Task export
*
* @access public
*/
public function tasks()
{
$this->common('taskExport', 'export', t('Tasks'), 'tasks', t('Tasks Export'));
}
/**
* Subtask export
*
* @access public
*/
public function subtasks()
{
$this->common('subtaskExport', 'export', t('Subtasks'), 'subtasks', t('Subtasks Export'));
}
/**
* Daily project summary export
*
* @access public
*/
public function summary()
{
$this->common('projectDailySummary', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
}
}

View File

@@ -56,72 +56,6 @@ class Project extends Base
))); )));
} }
/**
* Task export
*
* @access public
*/
public function exportTasks()
{
$project = $this->getProject();
$from = $this->request->getStringParam('from');
$to = $this->request->getStringParam('to');
if ($from && $to) {
$data = $this->taskExport->export($project['id'], $from, $to);
$this->response->forceDownload('Tasks_'.date('Y_m_d_H_i').'.csv');
$this->response->csv($data);
}
$this->response->html($this->projectLayout('project/export_tasks', array(
'values' => array(
'controller' => 'project',
'action' => 'exportTasks',
'project_id' => $project['id'],
'from' => $from,
'to' => $to,
),
'errors' => array(),
'date_format' => $this->config->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats(),
'project' => $project,
'title' => t('Tasks Export')
)));
}
/**
* Daily project summary export
*
* @access public
*/
public function exportDailyProjectSummary()
{
$project = $this->getProject();
$from = $this->request->getStringParam('from');
$to = $this->request->getStringParam('to');
if ($from && $to) {
$data = $this->projectDailySummary->getAggregatedMetrics($project['id'], $from, $to);
$this->response->forceDownload('Daily_Summary_'.date('Y_m_d_H_i').'.csv');
$this->response->csv($data);
}
$this->response->html($this->projectLayout('project/export_daily_summary', array(
'values' => array(
'controller' => 'project',
'action' => 'exportDailyProjectSummary',
'project_id' => $project['id'],
'from' => $from,
'to' => $to,
),
'errors' => array(),
'date_format' => $this->config->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats(),
'project' => $project,
'title' => t('Daily project summary export')
)));
}
/** /**
* Public access management * Public access management
* *

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
'Project manager' => 'Chef de projet', 'Project manager' => 'Chef de projet',
'Project member' => 'Membre du projet', 'Project member' => 'Membre du projet',
'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Un chef de projet peut changer les paramètres du projet et possède plus de privilèges qu\'un utilisateur standard.', 'A project manager can change the settings of the project and have more privileges than a standard user.' => 'Un chef de projet peut changer les paramètres du projet et possède plus de privilèges qu\'un utilisateur standard.',
'Gitlab Issue' => 'Ticket Gitlab',
'Subtask Id' => 'Identifiant de la sous-tâche',
'Subtasks' => 'Sous-tâches',
'Subtasks Export' => 'Exportation des sous-tâches',
'Subtasks exportation for "%s"' => 'Exportation des sous-tâches pour le projet « %s »',
'Task Title' => 'Titre de la tâche',
'Untitled' => 'Sans nom',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -637,4 +637,11 @@ return array(
// 'Project manager' => '', // 'Project manager' => '',
// 'Project member' => '', // 'Project member' => '',
// 'A project manager can change the settings of the project and have more privileges than a standard user.' => '', // 'A project manager can change the settings of the project and have more privileges than a standard user.' => '',
// 'Gitlab Issue' => '',
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
// 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
); );

View File

@@ -50,7 +50,8 @@ class Acl extends Base
'analytic' => '*', 'analytic' => '*',
'board' => array('movecolumn', 'edit', 'update', 'add', 'remove'), 'board' => array('movecolumn', 'edit', 'update', 'add', 'remove'),
'category' => '*', 'category' => '*',
'project' => array('edit', 'update', 'exporttasks', 'exportdailyprojectsummary', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'), 'export' => array('tasks', 'subtasks', 'summary'),
'project' => array('edit', 'update', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'),
'swimlane' => '*', 'swimlane' => '*',
); );
@@ -179,7 +180,7 @@ class Acl extends Base
// Check project member permissions // Check project member permissions
if ($this->isMemberAction($controller, $action)) { if ($this->isMemberAction($controller, $action)) {
return $this->isMemberActionAllowed($project_id); return $project_id > 0 && $this->projectPermission->isMember($project_id, $this->userSession->getId());
} }
// Other applications actions are allowed // Other applications actions are allowed
@@ -188,11 +189,10 @@ class Acl extends Base
public function isManagerActionAllowed($project_id) public function isManagerActionAllowed($project_id)
{ {
if ($this->userSession->isAdmin()) {
return true;
}
return $project_id > 0 && $this->projectPermission->isManager($project_id, $this->userSession->getId()); return $project_id > 0 && $this->projectPermission->isManager($project_id, $this->userSession->getId());
} }
public function isMemberActionAllowed($project_id)
{
return $project_id > 0 && $this->projectPermission->isMember($project_id, $this->userSession->getId());
}
} }

View File

@@ -58,15 +58,11 @@ class SubTask extends Base
*/ */
public function getStatusList() public function getStatusList()
{ {
$status = array( return array(
self::STATUS_TODO => t('Todo'), self::STATUS_TODO => t('Todo'),
self::STATUS_INPROGRESS => t('In progress'), self::STATUS_INPROGRESS => t('In progress'),
self::STATUS_DONE => t('Done'), self::STATUS_DONE => t('Done'),
); );
asort($status);
return $status;
} }
/** /**

119
app/Model/SubtaskExport.php Normal file
View File

@@ -0,0 +1,119 @@
<?php
namespace Model;
/**
* Subtask Export
*
* @package model
* @author Frederic Guillot
*/
class SubtaskExport extends Base
{
/**
* Subtask statuses
*
* @access private
* @var array
*/
private $subtask_status = array();
/**
* Fetch subtasks 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)
{
$this->subtask_status = $this->subTask->getStatusList();
$subtasks = $this->getSubtasks($project_id, $from, $to);
$results = array($this->getColumns());
foreach ($subtasks as $subtask) {
$results[] = $this->format($subtask);
}
return $results;
}
/**
* Get column titles
*
* @access public
* @return string[]
*/
public function getColumns()
{
return array(
e('Subtask Id'),
e('Title'),
e('Status'),
e('Assignee'),
e('Time estimated'),
e('Time spent'),
e('Task Id'),
e('Task Title'),
);
}
/**
* Format the output of a subtask array
*
* @access public
* @param array $subtask Subtask properties
* @return array
*/
public function format(array $subtask)
{
$values = array();
$values[] = $subtask['id'];
$values[] = $subtask['title'];
$values[] = $this->subtask_status[$subtask['status']];
$values[] = $subtask['assignee_name'] ?: $subtask['assignee_username'];
$values[] = $subtask['time_estimated'];
$values[] = $subtask['time_spent'];
$values[] = $subtask['task_id'];
$values[] = $subtask['task_title'];
return $values;
}
/**
* Get all subtasks for a given project
*
* @access public
* @param integer $task_id Task 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 getSubtasks($project_id, $from, $to)
{
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)));
}
return $this->db->table(SubTask::TABLE)
->eq('project_id', $project_id)
->columns(
SubTask::TABLE.'.*',
User::TABLE.'.username AS assignee_username',
User::TABLE.'.name AS assignee_name',
Task::TABLE.'.title AS task_title'
)
->gte('date_creation', $from)
->lte('date_creation', $to)
->join(Task::TABLE, 'id', 'task_id')
->join(User::TABLE, 'id', 'user_id')
->asc(SubTask::TABLE.'.id')
->findAll();
}
}

View File

@@ -32,6 +32,7 @@ class ClassProvider implements ServiceProviderInterface
'ProjectPermission', 'ProjectPermission',
'SubTask', 'SubTask',
'SubtaskPaginator', 'SubtaskPaginator',
'SubtaskExport',
'Swimlane', 'Swimlane',
'Task', 'Task',
'TaskCreation', 'TaskCreation',

View File

@@ -0,0 +1,24 @@
<div class="page-header">
<h2>
<?= t('Subtasks exportation for "%s"', $project['name']) ?>
</h2>
</div>
<form method="get" action="?" autocomplete="off">
<?= $this->formHidden('controller', $values) ?>
<?= $this->formHidden('action', $values) ?>
<?= $this->formHidden('project_id', $values) ?>
<?= $this->formLabel(t('Start Date'), 'from') ?>
<?= $this->formText('from', $values, $errors, array('required', 'placeholder="'.$this->inList($date_format, $date_formats).'"'), 'form-date') ?><br/>
<?= $this->formLabel(t('End Date'), 'to') ?>
<?= $this->formText('to', $values, $errors, array('required', 'placeholder="'.$this->inList($date_format, $date_formats).'"'), 'form-date') ?>
<div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div>
<div class="form-actions">
<input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"/>
</div>
</form>

View File

@@ -54,10 +54,13 @@
<h2><?= t('Exports') ?></h2> <h2><?= t('Exports') ?></h2>
<ul> <ul>
<li> <li>
<?= $this->a(t('Tasks'), 'project', 'exportTasks', array('project_id' => $project['id'])) ?> <?= $this->a(t('Tasks'), 'export', 'tasks', array('project_id' => $project['id'])) ?>
</li> </li>
<li> <li>
<?= $this->a(t('Daily project summary'), 'project', 'exportDailyProjectSummary', array('project_id' => $project['id'])) ?> <?= $this->a(t('Subtasks'), 'export', 'subtasks', array('project_id' => $project['id'])) ?>
</li>
<li>
<?= $this->a(t('Daily project summary'), 'export', 'summary', array('project_id' => $project['id'])) ?>
</li> </li>
</ul> </ul>
<?php endif ?> <?php endif ?>

View File

@@ -33,6 +33,7 @@ Available commands:
list Lists commands list Lists commands
export export
export:daily-project-summary Daily project summary CSV export (number of tasks per column and per day) export:daily-project-summary Daily project summary CSV export (number of tasks per column and per day)
export:subtasks Subtasks CSV export
export:tasks Tasks CSV export export:tasks Tasks CSV export
notification notification
notification:overdue-tasks Send notifications for overdue tasks notification:overdue-tasks Send notifications for overdue tasks
@@ -43,6 +44,20 @@ projects
Available commands Available commands
------------------ ------------------
### Subtasks CSV export
Usage:
```bash
./kanboard export:subtasks <project_id> <start_date> <end_date>
```
Example:
```bash
./kanboard export:subtasks 1 2014-10-01 2014-11-30 > /tmp/my_custom_export.csv
```
### Tasks CSV export ### Tasks CSV export
Usage: Usage:

View File

@@ -7,6 +7,7 @@ use Symfony\Component\Console\Application;
$application = new Application('Kanboard', APP_VERSION); $application = new Application('Kanboard', APP_VERSION);
$application->add(new Console\TaskOverdueNotification($container)); $application->add(new Console\TaskOverdueNotification($container));
$application->add(new Console\SubtaskExport($container));
$application->add(new Console\TaskExport($container)); $application->add(new Console\TaskExport($container));
$application->add(new Console\ProjectDailySummaryCalculation($container)); $application->add(new Console\ProjectDailySummaryCalculation($container));
$application->add(new Console\ProjectDailySummaryExport($container)); $application->add(new Console\ProjectDailySummaryExport($container));