(refs #26) Implements the dashboard issue display.

This commit is contained in:
shimamoto
2013-07-30 10:47:46 +09:00
parent 57109dd72e
commit 8409384232
5 changed files with 51 additions and 47 deletions

View File

@@ -33,13 +33,21 @@ trait DashboardControllerBase extends ControllerBase {
session.put(sessionKey, condition) session.put(sessionKey, condition)
val repositories = getAccessibleRepositories(context.loginAccount, baseUrl) val userName = context.loginAccount.get.userName
val repositories = getUserRepositories(userName, baseUrl).map(repo => repo.owner -> repo.name)
val filterUser = Map(filter -> userName)
val page = IssueSearchCondition.page(request)
// //
dashboard.html.issues( dashboard.html.issues(
issues.html.listparts(Nil, 0, 0, 0, condition), issues.html.listparts(
0, searchIssue(condition, filterUser, (page - 1) * IssueLimit, IssueLimit, repositories: _*),
0, page,
0, countIssue(condition.copy(state = "open"), filterUser, repositories: _*),
countIssue(condition.copy(state = "closed"), filterUser, repositories: _*),
condition),
countIssue(condition, Map.empty, repositories: _*),
countIssue(condition, Map("assigned" -> userName), repositories: _*),
countIssue(condition, Map("created_by" -> userName), repositories: _*),
repositories, repositories,
condition, condition,
filter) filter)

View File

@@ -301,16 +301,10 @@ trait IssuesControllerBase extends ControllerBase {
private def searchIssues(filter: String, repository: RepositoryService.RepositoryInfo) = { private def searchIssues(filter: String, repository: RepositoryService.RepositoryInfo) = {
val owner = repository.owner val owner = repository.owner
val repoName = repository.name val repoName = repository.name
val userName = if(filter != "all") Some(params("userName")) else None val filterUser = Map(filter -> params.getOrElse("userName", ""))
val page = IssueSearchCondition.page(request)
val sessionKey = s"${owner}/${repoName}/issues" val sessionKey = s"${owner}/${repoName}/issues"
val page = try {
val i = params.getOrElse("page", "1").toInt
if(i <= 0) 1 else i
} catch {
case e: NumberFormatException => 1
}
// retrieve search condition // retrieve search condition
val condition = if(request.getQueryString == null){ val condition = if(request.getQueryString == null){
session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition] session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
@@ -319,17 +313,17 @@ trait IssuesControllerBase extends ControllerBase {
session.put(sessionKey, condition) session.put(sessionKey, condition)
issues.html.list( issues.html.list(
searchIssue(owner, repoName, condition, filter, userName, (page - 1) * IssueLimit, IssueLimit), searchIssue(condition, filterUser, (page - 1) * IssueLimit, IssueLimit, owner -> repoName),
page, page,
(getCollaborators(owner, repoName) :+ owner).sorted, (getCollaborators(owner, repoName) :+ owner).sorted,
getMilestones(owner, repoName), getMilestones(owner, repoName),
getLabels(owner, repoName), getLabels(owner, repoName),
countIssue(owner, repoName, condition.copy(state = "open"), filter, userName), countIssue(condition.copy(state = "open"), filterUser, owner -> repoName),
countIssue(owner, repoName, condition.copy(state = "closed"), filter, userName), countIssue(condition.copy(state = "closed"), filterUser, owner -> repoName),
countIssue(owner, repoName, condition, "all", None), countIssue(condition, Map.empty, owner -> repoName),
context.loginAccount.map(x => countIssue(owner, repoName, condition, "assigned", Some(x.userName))), context.loginAccount.map(x => countIssue(condition, Map("assigned" -> x.userName), owner -> repoName)),
context.loginAccount.map(x => countIssue(owner, repoName, condition, "created_by", Some(x.userName))), context.loginAccount.map(x => countIssue(condition, Map("created_by" -> x.userName), owner -> repoName)),
countIssueGroupByLabels(owner, repoName, condition, filter, userName), countIssueGroupByLabels(owner, repoName, condition, filterUser),
condition, condition,
filter, filter,
repository, repository,

View File

@@ -42,18 +42,16 @@ trait IssuesService {
/** /**
* Returns the count of the search result against issues. * Returns the count of the search result against issues.
* *
* @param owner the repository owner
* @param repository the repository name
* @param condition the search condition * @param condition the search condition
* @param filter the filter type ("all", "assigned" or "created_by") * @param filterUser the filter user name (key is "all", "assigned" or "created_by", value is the user name)
* @param userName the filter user name required for "assigned" and "created_by" * @param repos Tuple of the repository owner and the repository name
* @return the count of the search result * @return the count of the search result
*/ */
def countIssue(owner: String, repository: String, condition: IssueSearchCondition, filter: String, userName: Option[String]): Int = { def countIssue(condition: IssueSearchCondition, filterUser: Map[String, String], repos: (String, String)*): Int = {
// TODO It must be _.length instead of map (_.issueId) list).length. // TODO It must be _.length instead of map (_.issueId) list).length.
// But it does not work on Slick 1.0.1 (worked on Slick 1.0.0). // But it does not work on Slick 1.0.1 (worked on Slick 1.0.0).
// https://github.com/slick/slick/issues/170 // https://github.com/slick/slick/issues/170
(searchIssueQuery(owner, repository, condition, filter, userName) map (_.issueId) list).length (searchIssueQuery(repos, condition, filterUser) map (_.issueId) list).length
} }
/** /**
* Returns the Map which contains issue count for each labels. * Returns the Map which contains issue count for each labels.
@@ -61,14 +59,13 @@ trait IssuesService {
* @param owner the repository owner * @param owner the repository owner
* @param repository the repository name * @param repository the repository name
* @param condition the search condition * @param condition the search condition
* @param filter the filter type ("all", "assigned" or "created_by") * @param filterUser the filter user name (key is "all", "assigned" or "created_by", value is the user name)
* @param userName the filter user name required for "assigned" and "created_by" * @return the Map which contains issue count for each labels (key is label name, value is issue count)
* @return the Map which contains issue count for each labels (key is label name, value is issue count),
*/ */
def countIssueGroupByLabels(owner: String, repository: String, condition: IssueSearchCondition, def countIssueGroupByLabels(owner: String, repository: String, condition: IssueSearchCondition,
filter: String, userName: Option[String]): Map[String, Int] = { filterUser: Map[String, String]): Map[String, Int] = {
searchIssueQuery(owner, repository, condition.copy(labels = Set.empty), filter, userName) searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), filterUser)
.innerJoin(IssueLabels).on { (t1, t2) => .innerJoin(IssueLabels).on { (t1, t2) =>
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
} }
@@ -87,20 +84,18 @@ trait IssuesService {
/** /**
* Returns the search result against issues. * Returns the search result against issues.
* *
* @param owner the repository owner
* @param repository the repository name
* @param condition the search condition * @param condition the search condition
* @param filter the filter type ("all", "assigned" or "created_by") * @param filterUser the filter user name (key is "all", "assigned" or "created_by", value is the user name)
* @param userName the filter user name required for "assigned" and "created_by"
* @param offset the offset for pagination * @param offset the offset for pagination
* @param limit the limit for pagination * @param limit the limit for pagination
* @param repos Tuple of the repository owner and the repository name
* @return the search result (list of tuples which contain issue, labels and comment count) * @return the search result (list of tuples which contain issue, labels and comment count)
*/ */
def searchIssue(owner: String, repository: String, condition: IssueSearchCondition, def searchIssue(condition: IssueSearchCondition, filterUser: Map[String, String],
filter: String, userName: Option[String], offset: Int, limit: Int): List[(Issue, List[Label], Int)] = { offset: Int, limit: Int, repos: (String, String)*): List[(Issue, List[Label], Int)] = {
// get issues and comment count and labels // get issues and comment count and labels
searchIssueQuery(owner, repository, condition, filter, userName) searchIssueQuery(repos, condition, filterUser)
.innerJoin(IssueOutline).on { (t1, t2) => t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) } .innerJoin(IssueOutline).on { (t1, t2) => t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) }
.leftJoin (IssueLabels) .on { case ((t1, t2), t3) => t1.byIssue(t3.userName, t3.repositoryName, t3.issueId) } .leftJoin (IssueLabels) .on { case ((t1, t2), t3) => t1.byIssue(t3.userName, t3.repositoryName, t3.issueId) }
.leftJoin (Labels) .on { case (((t1, t2), t3), t4) => t3.byLabel(t4.userName, t4.repositoryName, t4.labelId) } .leftJoin (Labels) .on { case (((t1, t2), t3), t4) => t3.byLabel(t4.userName, t4.repositoryName, t4.labelId) }
@@ -136,14 +131,14 @@ trait IssuesService {
/** /**
* Assembles query for conditional issue searching. * Assembles query for conditional issue searching.
*/ */
private def searchIssueQuery(owner: String, repository: String, condition: IssueSearchCondition, filter: String, userName: Option[String]) = private def searchIssueQuery(repos: Seq[(String, String)], condition: IssueSearchCondition, filterUser: Map[String, String]) =
Query(Issues) filter { t1 => Query(Issues) filter { t1 =>
(t1.byRepository(owner, repository)) && (repos.map { case (owner, repository) => t1.byRepository(owner, repository) } reduceLeft ( _ || _ ) ) &&
(t1.closed is (condition.state == "closed").bind) && (t1.closed is (condition.state == "closed").bind) &&
(t1.milestoneId is condition.milestoneId.get.get.bind, condition.milestoneId.flatten.isDefined) && (t1.milestoneId is condition.milestoneId.get.get.bind, condition.milestoneId.flatten.isDefined) &&
(t1.milestoneId isNull, condition.milestoneId == Some(None)) && (t1.milestoneId isNull, condition.milestoneId == Some(None)) &&
(t1.assignedUserName is userName.get.bind, filter == "assigned") && (t1.assignedUserName is filterUser("assigned").bind, filterUser.get("assigned").isDefined) &&
(t1.openedUserName is userName.get.bind, filter == "created_by") && (t1.openedUserName is filterUser("created_by").bind, filterUser.get("created_by").isDefined) &&
(IssueLabels filter { t2 => (IssueLabels filter { t2 =>
(t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) && (t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) &&
(t2.labelId in (t2.labelId in
@@ -329,6 +324,13 @@ object IssuesService {
param(request, "state", Seq("open", "closed")).getOrElse("open"), param(request, "state", Seq("open", "closed")).getOrElse("open"),
param(request, "sort", Seq("created", "comments", "updated")).getOrElse("created"), param(request, "sort", Seq("created", "comments", "updated")).getOrElse("created"),
param(request, "direction", Seq("asc", "desc")).getOrElse("desc")) param(request, "direction", Seq("asc", "desc")).getOrElse("desc"))
def page(request: HttpServletRequest) = try {
val i = param(request, "page").getOrElse("1").toInt
if(i <= 0) 1 else i
} catch {
case e: NumberFormatException => 1
}
} }
} }

View File

@@ -2,7 +2,7 @@
allCount: Int, allCount: Int,
assignedCount: Int, assignedCount: Int,
createdByCount: Int, createdByCount: Int,
repositories: List[service.RepositoryService.RepositoryInfo], repositories: List[(String, String)],
condition: service.IssuesService.IssueSearchCondition, condition: service.IssuesService.IssueSearchCondition,
filter: String)(implicit context: app.Context) filter: String)(implicit context: app.Context)
@import context._ @import context._
@@ -33,11 +33,11 @@
</ul> </ul>
<hr/> <hr/>
<ul class="nav nav-pills nav-stacked small"> <ul class="nav nav-pills nav-stacked small">
@repositories.map { repository => @repositories.map { case (owner, name) =>
<li> <li>
<a href="@condition.copy(repo = Some(repository.owner + "/" + repository.name)).toURL"> <a href="@condition.copy(repo = Some(owner + "/" + name)).toURL">
<span class="count-right">0</span> <span class="count-right">0</span>
@repository.owner/@repository.name @owner/@name
</a> </a>
</li> </li>
} }

View File

@@ -146,7 +146,7 @@
<input type="checkbox" value="@issue.issueId"/> <input type="checkbox" value="@issue.issueId"/>
} }
@if(repository.isEmpty){ @if(repository.isEmpty){
<a href="@path/@issue.userName/@issue.repositoryName">@issue.repositoryName</a>&nbsp;&#xFF65;&nbsp; <a href="@path/@issue.userName/@issue.repositoryName">@issue.repositoryName</a>&nbsp;&#xFF65;
} }
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-title">@issue.title</a> <a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-title">@issue.title</a>
@labels.map { label => @labels.map { label =>