From 69ae1c08effd703e674afe935898fad397a768da Mon Sep 17 00:00:00 2001 From: William MARTIN Date: Wed, 15 Jun 2011 09:11:47 +0200 Subject: [PATCH 1/8] Add an sub-tab under Issue to add a summury view --- src/IDF/Views/Issue.php | 62 ++++++++++++++++ src/IDF/conf/urls.php | 5 ++ src/IDF/templates/idf/issues/base.html | 3 +- src/IDF/templates/idf/issues/summary.html | 89 +++++++++++++++++++++++ www/media/idf/css/style.css | 46 ++++++++++++ 5 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 src/IDF/templates/idf/issues/summary.html diff --git a/src/IDF/Views/Issue.php b/src/IDF/Views/Issue.php index 91fdc40..33f768c 100644 --- a/src/IDF/Views/Issue.php +++ b/src/IDF/Views/Issue.php @@ -77,6 +77,68 @@ class IDF_Views_Issue $params, $request); } + /** + * View the issue summary. + * TODO Add thoses data in cache, and process it only after an issue update + */ + public $summary_precond = array('IDF_Precondition::accessIssues'); + public function summary($request, $match) + { + $prj = $request->project; + $opened = $prj->getIssueCountByStatus('open'); + $closed = $prj->getIssueCountByStatus('closed'); + $otags = implode(',', $prj->getTagIdsByStatus('open')); + + // Issue status statistics + $status = array(); + $status['Open'] = array($opened, (int)(100 * $opened / ($opened + $closed))); + $status['Closed'] = array($closed, (int)(100 * $closed / ($opened + $closed))); + + // Issue owner statistics + $sqlIssueTable = Pluf::factory('IDF_Issue')->getSqlTable(); + $sqlUsersTable = Pluf::factory('Pluf_User')->getSqlTable(); + $query = <<<"QUERY" +SELECT CONCAT(first_name, " ", last_name) as name, nb FROM (SELECT uid as id,count(uid) as nb FROM (SELECT ifnull(owner, -1) as uid FROM $sqlIssueTable WHERE status IN ($otags)) as ff group by uid) AS ff LEFT JOIN $sqlUsersTable using(id) +QUERY; + $db = Pluf::db(); + $dbData = $db->select($query); + $ownerStatistics = array(); + foreach ($dbData as $k => $v) { + $key = ($v['name'] === null) ? __('Not assigned') : $v['name']; + $ownerStatistics[$key] = array($v['nb'], (int)(100 * $v['nb'] / $opened)); + } + + // Issue class tag statistics + $tags = $prj->getTagCloud(); + $tagStatistics = array(); + foreach ($tags as $t) { + $tagStatistics[$t->class][$t->name] = array($t->nb_use, $t->id); + } + foreach($tagStatistics as $k => $v) { + $nbIssueInClass = 0; + foreach ($v as $val) { + $nbIssueInClass += $val[0]; + } + foreach ($v as $kk => $vv) { + $tagStatistics[$k][$kk] = array($vv[0], (int)(100 * $vv[0] / $nbIssueInClass), $vv[1]); + } + } + + // Sort + krsort($tagStatistics); + arsort($ownerStatistics); + + $title = sprintf(__('Summary of tracked issues in %s.'), (string) $prj); + return Pluf_Shortcuts_RenderToResponse('idf/issues/summary.html', + array('page_title' => $title, + 'project' => $prj, + 'tagStatistics' => $tagStatistics, + 'ownerStatistics' => $ownerStatistics, + 'status' => $status, + ), + $request); + } + /** * View the issues watch list of a given user. * Limited to a specified project diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php index 55c47fa..03779c0 100644 --- a/src/IDF/conf/urls.php +++ b/src/IDF/conf/urls.php @@ -117,6 +117,11 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/$#', 'base' => $base, 'model' => 'IDF_Views_Issue', 'method' => 'index'); + +$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/summary/$#', + 'base' => $base, + 'model' => 'IDF_Views_Issue', + 'method' => 'summary'); $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/search/$#', 'base' => $base, diff --git a/src/IDF/templates/idf/issues/base.html b/src/IDF/templates/idf/issues/base.html index 779de34..636d5da 100644 --- a/src/IDF/templates/idf/issues/base.html +++ b/src/IDF/templates/idf/issues/base.html @@ -2,7 +2,8 @@ {block tabissues} class="active"{/block} {block subtabs}
-{trans 'Open Issues'} +{trans 'Summary'} +| {trans 'Open Issues'} {if !$user.isAnonymous()} | {trans 'New Issue'} | {trans 'My Issues'} | {trans 'My watch list'}{/if} |
diff --git a/src/IDF/templates/idf/issues/summary.html b/src/IDF/templates/idf/issues/summary.html new file mode 100644 index 0000000..14765e5 --- /dev/null +++ b/src/IDF/templates/idf/issues/summary.html @@ -0,0 +1,89 @@ +{extends "idf/issues/base.html"} + +{block docclass}yui-t2{assign $inSummaryIssues=true}{/block} + +{block body} +
+{foreach $tagStatistics as $key => $class} +
+

Unresolved: By {$key}

+ + +{foreach $class as $key => $value} + + + + + +{/foreach} + +
{$key}{$value[0]} + + + + + + +
+
+
   {$value[1]}%
+
+
+{/foreach} +
+ +
+ +
+

Status Summary

+ + +{foreach $status as $key => $value} + + + + + +{/foreach} + +
{$key}{$value[0]} + + + + + + +
+
+
   {$value[1]}%
+
+
+ +
+

Unresolved: By Assignee

+ + +{foreach $ownerStatistics as $key => $value} + + + + + +{/foreach} + +
{$key}{$value[0]} + + + + + + +
+
+
   {$value[1]}%
+
+
+ +
+ +{/block} diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css index 3480828..be16143 100644 --- a/www/media/idf/css/style.css +++ b/www/media/idf/css/style.css @@ -1104,3 +1104,49 @@ div.p-list-private { right: -3px; position: relative; } + +/* + * Issue summary + */ +div.issue-summary { + float: left; + width: 50%; +} + +div.issue-summary > div { + margin-right: 3em; + padding-top: 1em; +} + +div.issue-summary h2 { + border-bottom: 1px solid #A5E26A; +} + +table.issue-summary { + width: 100%; +} + +table.issue-summary tr td { + border: 0; + padding: .1em; +} + +table.issue-summary td.graph { + width: 60%; +} + +table.issue-summary td.count { + text-align: right; + padding-right: .5em; +} + +table.graph { + width: 100%; + margin: 0; + padding: 0; +} + +td.graph-color { + background: #3C78B5; +} + From ef5b93e3f77fc280ff820a632d3a2c1d8084444b Mon Sep 17 00:00:00 2001 From: William MARTIN Date: Wed, 15 Jun 2011 11:18:41 +0200 Subject: [PATCH 2/8] Fix the special case of a empty issue tracker, for the issue summary. --- src/IDF/Views/Issue.php | 81 +++++++----- src/IDF/templates/idf/issues/summary.html | 144 +++++++++++----------- www/media/idf/css/style.css | 2 +- 3 files changed, 123 insertions(+), 104 deletions(-) diff --git a/src/IDF/Views/Issue.php b/src/IDF/Views/Issue.php index 33f768c..34b008b 100644 --- a/src/IDF/Views/Issue.php +++ b/src/IDF/Views/Issue.php @@ -84,53 +84,66 @@ class IDF_Views_Issue public $summary_precond = array('IDF_Precondition::accessIssues'); public function summary($request, $match) { + $tagStatistics = array(); + $ownerStatistics = array(); + $status = array(); + $isTrackerEmpty = false; + $prj = $request->project; $opened = $prj->getIssueCountByStatus('open'); $closed = $prj->getIssueCountByStatus('closed'); - $otags = implode(',', $prj->getTagIdsByStatus('open')); - // Issue status statistics - $status = array(); - $status['Open'] = array($opened, (int)(100 * $opened / ($opened + $closed))); - $status['Closed'] = array($closed, (int)(100 * $closed / ($opened + $closed))); - - // Issue owner statistics - $sqlIssueTable = Pluf::factory('IDF_Issue')->getSqlTable(); - $sqlUsersTable = Pluf::factory('Pluf_User')->getSqlTable(); - $query = <<<"QUERY" + // Check if the tracker is empty + if ($opened === 0 && $closed === 0) { + $isTrackerEmpty = true; + } else { + if ($opened > 0 || $closed > 0) { + // Issue status statistics + $status['Open'] = array($opened, (int)(100 * $opened / ($opened + $closed))); + $status['Closed'] = array($closed, (int)(100 * $closed / ($opened + $closed))); + } + + if ($opened > 0) { + // Issue owner statistics + $sqlIssueTable = Pluf::factory('IDF_Issue')->getSqlTable(); + $sqlUsersTable = Pluf::factory('Pluf_User')->getSqlTable(); + $otags = implode(',', $prj->getTagIdsByStatus('open')); + $query = <<<"QUERY" SELECT CONCAT(first_name, " ", last_name) as name, nb FROM (SELECT uid as id,count(uid) as nb FROM (SELECT ifnull(owner, -1) as uid FROM $sqlIssueTable WHERE status IN ($otags)) as ff group by uid) AS ff LEFT JOIN $sqlUsersTable using(id) QUERY; - $db = Pluf::db(); - $dbData = $db->select($query); - $ownerStatistics = array(); - foreach ($dbData as $k => $v) { - $key = ($v['name'] === null) ? __('Not assigned') : $v['name']; - $ownerStatistics[$key] = array($v['nb'], (int)(100 * $v['nb'] / $opened)); - } + $db = Pluf::db(); + $dbData = $db->select($query); + foreach ($dbData as $k => $v) { + $key = ($v['name'] === null) ? __('Not assigned') : $v['name']; + $ownerStatistics[$key] = array($v['nb'], (int)(100 * $v['nb'] / $opened)); + } - // Issue class tag statistics - $tags = $prj->getTagCloud(); - $tagStatistics = array(); - foreach ($tags as $t) { - $tagStatistics[$t->class][$t->name] = array($t->nb_use, $t->id); - } - foreach($tagStatistics as $k => $v) { - $nbIssueInClass = 0; - foreach ($v as $val) { - $nbIssueInClass += $val[0]; - } - foreach ($v as $kk => $vv) { - $tagStatistics[$k][$kk] = array($vv[0], (int)(100 * $vv[0] / $nbIssueInClass), $vv[1]); + // Issue class tag statistics + $tags = $prj->getTagCloud(); + foreach ($tags as $t) { + $tagStatistics[$t->class][$t->name] = array($t->nb_use, $t->id); + } + foreach($tagStatistics as $k => $v) { + $nbIssueInClass = 0; + foreach ($v as $val) { + $nbIssueInClass += $val[0]; + } + foreach ($v as $kk => $vv) { + $tagStatistics[$k][$kk] = array($vv[0], (int)(100 * $vv[0] / $nbIssueInClass), $vv[1]); + } + } + + // Sort + krsort($tagStatistics); + arsort($ownerStatistics); } } - // Sort - krsort($tagStatistics); - arsort($ownerStatistics); - $title = sprintf(__('Summary of tracked issues in %s.'), (string) $prj); + return Pluf_Shortcuts_RenderToResponse('idf/issues/summary.html', array('page_title' => $title, + 'trackerEmpty' => $isTrackerEmpty, 'project' => $prj, 'tagStatistics' => $tagStatistics, 'ownerStatistics' => $ownerStatistics, diff --git a/src/IDF/templates/idf/issues/summary.html b/src/IDF/templates/idf/issues/summary.html index 14765e5..578a77d 100644 --- a/src/IDF/templates/idf/issues/summary.html +++ b/src/IDF/templates/idf/issues/summary.html @@ -3,87 +3,93 @@ {block docclass}yui-t2{assign $inSummaryIssues=true}{/block} {block body} +{if $trackerEmpty} + {aurl 'create_url', 'IDF_Views_Issue::create', array($project.shortname)} +

{blocktrans}The issue tracker is empty
You can create your first issue here.{/blocktrans}

+{else}
{foreach $tagStatistics as $key => $class} -
-

Unresolved: By {$key}

- - -{foreach $class as $key => $value} - - - - - -{/foreach} - -
{$key}{$value[0]} - - - - - +
+

Unresolved: By {$key}

+
-
-
   {$value[1]}%
+ + {foreach $class as $key => $value} + + + + + + {/foreach}
{$key}{$value[0]} + + + + + + +
+
+
   {$value[1]}%
+
-
-
+
{/foreach}
- -
-

Status Summary

- - -{foreach $status as $key => $value} - - - - - -{/foreach} - -
{$key}{$value[0]} - - - - - +{if $status} +
+

Status Summary

+
-
-
   {$value[1]}%
+ + {foreach $status as $key => $value} + + + + + + {/foreach}
{$key}{$value[0]} + + + + + + +
+
+
   {$value[1]}%
+
-
-
+
+{/if} -
-

Unresolved: By Assignee

- - -{foreach $ownerStatistics as $key => $value} - - - - - -{/foreach} - -
{$key}{$value[0]} - - - - - +{if $ownerStatistics} +
+

Unresolved: By Assignee

+
-
-
   {$value[1]}%
+ + {foreach $ownerStatistics as $key => $value} + + + + + + {/foreach}
{$key}{$value[0]} + + + + + + +
+
+
   {$value[1]}%
+
-
+
+{/if} - - - +{/if} {/block} diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css index be16143..2adbc8e 100644 --- a/www/media/idf/css/style.css +++ b/www/media/idf/css/style.css @@ -1128,7 +1128,7 @@ table.issue-summary { table.issue-summary tr td { border: 0; - padding: .1em; + padding: .1em .005em; } table.issue-summary td.graph { From b03d7a04a03ae1c1f19c617c7ebecb573b5152d7 Mon Sep 17 00:00:00 2001 From: William MARTIN Date: Wed, 15 Jun 2011 11:30:11 +0200 Subject: [PATCH 3/8] improve rendering of 0% bar --- src/IDF/templates/idf/issues/summary.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IDF/templates/idf/issues/summary.html b/src/IDF/templates/idf/issues/summary.html index 578a77d..78f2678 100644 --- a/src/IDF/templates/idf/issues/summary.html +++ b/src/IDF/templates/idf/issues/summary.html @@ -20,7 +20,7 @@ - @@ -49,7 +49,7 @@ - +
+
   {$value[1]}% - @@ -76,7 +76,7 @@ - +
+
   {$value[1]}% - From dab8ea63fc4651f497f0329b567f383020a2ef0c Mon Sep 17 00:00:00 2001 From: William MARTIN Date: Wed, 15 Jun 2011 11:35:30 +0200 Subject: [PATCH 4/8] Mark string for translation --- src/IDF/templates/idf/issues/summary.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/IDF/templates/idf/issues/summary.html b/src/IDF/templates/idf/issues/summary.html index 78f2678..b50fbe0 100644 --- a/src/IDF/templates/idf/issues/summary.html +++ b/src/IDF/templates/idf/issues/summary.html @@ -5,12 +5,12 @@ {block body} {if $trackerEmpty} {aurl 'create_url', 'IDF_Views_Issue::create', array($project.shortname)} -

{blocktrans}The issue tracker is empty
You can create your first issue here.{/blocktrans}

+

{blocktrans}The issue tracker is empty.
You can create your first issue here.{/blocktrans}

{else}
{foreach $tagStatistics as $key => $class}
-

Unresolved: By {$key}

+

{blocktrans}Unresolved: By {$key}{/blocktrans}

+
   {$value[1]}%
{foreach $class as $key => $value} @@ -39,7 +39,7 @@
{if $status}
-

Status Summary

+

{blocktrans}Status Summary{/blocktrans}

{foreach $status as $key => $value} @@ -66,7 +66,7 @@ {if $ownerStatistics}
-

Unresolved: By Assignee

+

{blocktrans}Unresolved: By Assignee{/blocktrans}

{foreach $ownerStatistics as $key => $value} From 5427aab4569cd7c974bc86b54915ce1ae56e1bb7 Mon Sep 17 00:00:00 2001 From: William MARTIN Date: Wed, 15 Jun 2011 13:52:58 +0200 Subject: [PATCH 5/8] Change "ifnull" sql function to "coalesce" which is supported by postgresql --- src/IDF/Views/Issue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IDF/Views/Issue.php b/src/IDF/Views/Issue.php index 34b008b..22c1e2e 100644 --- a/src/IDF/Views/Issue.php +++ b/src/IDF/Views/Issue.php @@ -109,7 +109,7 @@ class IDF_Views_Issue $sqlUsersTable = Pluf::factory('Pluf_User')->getSqlTable(); $otags = implode(',', $prj->getTagIdsByStatus('open')); $query = <<<"QUERY" -SELECT CONCAT(first_name, " ", last_name) as name, nb FROM (SELECT uid as id,count(uid) as nb FROM (SELECT ifnull(owner, -1) as uid FROM $sqlIssueTable WHERE status IN ($otags)) as ff group by uid) AS ff LEFT JOIN $sqlUsersTable using(id) +SELECT CONCAT(first_name, " ", last_name) as name, nb FROM (SELECT uid as id,count(uid) as nb FROM (SELECT coalesce(owner, -1) as uid FROM $sqlIssueTable WHERE status IN ($otags)) as ff group by uid) AS ff LEFT JOIN $sqlUsersTable using(id) QUERY; $db = Pluf::db(); $dbData = $db->select($query); From 6e7c9f7c4b6a6e03461e04b80edcf4867a71e6fc Mon Sep 17 00:00:00 2001 From: William MARTIN Date: Wed, 15 Jun 2011 17:29:29 +0200 Subject: [PATCH 6/8] Use css instead of   --- src/IDF/templates/idf/issues/summary.html | 6 +++--- www/media/idf/css/style.css | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/IDF/templates/idf/issues/summary.html b/src/IDF/templates/idf/issues/summary.html index b50fbe0..bfff813 100644 --- a/src/IDF/templates/idf/issues/summary.html +++ b/src/IDF/templates/idf/issues/summary.html @@ -23,7 +23,7 @@ - +
   {$value[1]}%{$value[1]}%
@@ -52,7 +52,7 @@
   {$value[1]}%{$value[1]}%
@@ -79,7 +79,7 @@
   {$value[1]}%{$value[1]}%
diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css index 2adbc8e..4b94eb2 100644 --- a/www/media/idf/css/style.css +++ b/www/media/idf/css/style.css @@ -1146,7 +1146,11 @@ table.graph { padding: 0; } -td.graph-color { +table.issue-summary td.graph-color { background: #3C78B5; } +table.issue-summary td.graph-percent { + padding-left: 1em; +} + From 6d55602ef38c50d748afa83ee305cc1f78a8c743 Mon Sep 17 00:00:00 2001 From: William MARTIN Date: Wed, 15 Jun 2011 17:30:23 +0200 Subject: [PATCH 7/8] Add IDF_Project::getIssueCountByOwner and use it into IDF_Views_Issue::summary --- src/IDF/Project.php | 41 +++++++++++++++++++++++++++++++++++++++++ src/IDF/Views/Issue.php | 20 +++++++++----------- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/IDF/Project.php b/src/IDF/Project.php index 2f09219..14f66c7 100644 --- a/src/IDF/Project.php +++ b/src/IDF/Project.php @@ -132,6 +132,47 @@ class IDF_Project extends Pluf_Model } return $projects[0]; } + + /** + * Returns the number of open/closed issues. + * + * @param string Status ('open'), 'closed' + * @param IDF_Tag Subfilter with a label (null) + * @return int Count + */ + public function getIssueCountByOwner($status='open') + { + switch ($status) { + case 'open': + $tags = implode(',', $this->getTagIdsByStatus('open')); + break; + case 'closed': + default: + $tags = implode(',', $this->getTagIdsByStatus('closed')); + break; + } + $sqlIssueTable = Pluf::factory('IDF_Issue')->getSqlTable(); + $query = <<<"QUERY" +SELECT uid AS id,COUNT(uid) AS nb +FROM ( + SELECT COALESCE(owner, -1) AS uid + FROM $sqlIssueTable + WHERE status IN ($tags) + ) AS ff +GROUP BY uid +QUERY; + $db = Pluf::db(); + $dbData = $db->select($query); + $ownerStatistics = array(); + foreach ($dbData as $k => $v) { + $key = ($v['id'] === '-1') ? null : $v['id']; + $ownerStatistics[$key] = (int)$v['nb']; + } + + arsort($ownerStatistics); + + return $ownerStatistics; + } /** * Returns the number of open/closed issues. diff --git a/src/IDF/Views/Issue.php b/src/IDF/Views/Issue.php index 22c1e2e..fc9d7b4 100644 --- a/src/IDF/Views/Issue.php +++ b/src/IDF/Views/Issue.php @@ -105,17 +105,15 @@ class IDF_Views_Issue if ($opened > 0) { // Issue owner statistics - $sqlIssueTable = Pluf::factory('IDF_Issue')->getSqlTable(); - $sqlUsersTable = Pluf::factory('Pluf_User')->getSqlTable(); - $otags = implode(',', $prj->getTagIdsByStatus('open')); - $query = <<<"QUERY" -SELECT CONCAT(first_name, " ", last_name) as name, nb FROM (SELECT uid as id,count(uid) as nb FROM (SELECT coalesce(owner, -1) as uid FROM $sqlIssueTable WHERE status IN ($otags)) as ff group by uid) AS ff LEFT JOIN $sqlUsersTable using(id) -QUERY; - $db = Pluf::db(); - $dbData = $db->select($query); - foreach ($dbData as $k => $v) { - $key = ($v['name'] === null) ? __('Not assigned') : $v['name']; - $ownerStatistics[$key] = array($v['nb'], (int)(100 * $v['nb'] / $opened)); + $owners = $prj->getIssueCountByOwner('open'); + foreach ($owners as $user => $nb) { + if ($user === '') { + $key = __('Not assigned'); + } else { + $obj = Pluf::factory('Pluf_User')->getOne(array('filter'=>'id='.$user)); + $key = $obj->first_name . ' ' . $obj->last_name; + } + $ownerStatistics[$key] = array($nb, (int)(100 * $nb / $opened)); } // Issue class tag statistics From bbc185bf3b8ec4dbabd78f97c65e7b5988309050 Mon Sep 17 00:00:00 2001 From: William MARTIN Date: Mon, 20 Jun 2011 11:34:42 +0200 Subject: [PATCH 8/8] Add unit test for IDF_Project::getIssueCountByOwner --- test/IDF/ProjectTest.php | 90 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 test/IDF/ProjectTest.php diff --git a/test/IDF/ProjectTest.php b/test/IDF/ProjectTest.php new file mode 100644 index 0000000..7881bbe --- /dev/null +++ b/test/IDF/ProjectTest.php @@ -0,0 +1,90 @@ +login = 'user1'; + $user1->create(); + $user2 = new Pluf_User(); + $user2->login = 'user2'; + $user2->create(); + + // Add a project + $prj = new IDF_Project(); + $prj->create(); + $tag = $prj->getTagIdsByStatus('open'); + + // First test with no issue + $stats = $prj->getIssueCountByOwner(); + $this->assertEquals($stats, array()); + + // Add some issues + $issue1 = new IDF_Issue(); + $issue1->project = $prj; + $issue1->submitter = $user1; + $issue1->owner = $user1; + $issue1->status = new IDF_Tag($tag[0]); + $issue1->create(); + + $issue2 = new IDF_Issue(); + $issue2->project = $prj; + $issue2->submitter = $user2; + $issue2->owner = $user1; + $issue2->status = new IDF_Tag($tag[0]); + $issue2->create(); + + $issue3 = new IDF_Issue(); + $issue3->project = $prj; + $issue3->submitter = $user2; + $issue3->status = new IDF_Tag($tag[0]); + $issue3->create(); + + $issue4 = new IDF_Issue(); + $issue4->project = $prj; + $issue4->submitter = $user2; + $issue4->owner = $user2; + $issue4->status = new IDF_Tag($tag[0]); + $issue4->create(); + + // 2nd test + $stats = $prj->getIssueCountByOwner(); + $expected = array(0 => 1, + $user2->id => 1, + $user1->id => 2); + $this->assertEquals($stats, $expected); + + // Clean DB + $issue4->delete(); + $issue3->delete(); + $issue2->delete(); + $issue1->delete(); + $prj->delete(); + $user2->delete(); + $user1->delete(); + } +} +