Sat, 30 May 2020 15:26:15 +0200
adds issue summaries
1.1 --- a/setup/postgres/psql_create_tables.sql Sun May 24 15:30:43 2020 +0200 1.2 +++ b/setup/postgres/psql_create_tables.sql Sat May 30 15:26:15 2020 +0200 1.3 @@ -41,7 +41,8 @@ 1.4 'InReview', 1.5 'Done', 1.6 'Rejected', 1.7 - 'Withdrawn' 1.8 + 'Withdrawn', 1.9 + 'Duplicate' 1.10 ); 1.11 1.12 create type issue_category as enum ( 1.13 @@ -52,6 +53,11 @@ 1.14 'Test' 1.15 ); 1.16 1.17 +create table lpit_issue_phases ( 1.18 + status issue_status primary key, 1.19 + phase integer not null 1.20 +); 1.21 + 1.22 create table lpit_issue ( 1.23 issueid serial primary key, 1.24 project integer not null references lpit_project(projectid),
2.1 --- a/setup/postgres/psql_default_data.sql Sun May 24 15:30:43 2020 +0200 2.2 +++ b/setup/postgres/psql_default_data.sql Sat May 30 15:26:15 2020 +0200 2.3 @@ -0,0 +1,10 @@ 2.4 +insert into lpit_issue_phases (status, phase) values 2.5 + ('InSpecification', 0), 2.6 + ('ToDo', 0), 2.7 + ('Scheduled', 1), 2.8 + ('InProgress', 1), 2.9 + ('InReview', 1), 2.10 + ('Done', 2), 2.11 + ('Rejected', 2), 2.12 + ('Withdrawn', 2), 2.13 + ('Duplicate', 2);
3.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Sun May 24 15:30:43 2020 +0200 3.2 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Sat May 30 15:26:15 2020 +0200 3.3 @@ -46,6 +46,7 @@ 3.4 public final class PGProjectDao implements ProjectDao { 3.5 3.6 private final PreparedStatement insert, update, list, find; 3.7 + private final PreparedStatement issue_summary; 3.8 3.9 public PGProjectDao(Connection connection) throws SQLException { 3.10 list = connection.prepareStatement( 3.11 @@ -62,6 +63,14 @@ 3.12 "left join lpit_user owner on lpit_project.owner = owner.userid " + 3.13 "where projectid = ?"); 3.14 3.15 + issue_summary = connection.prepareStatement( 3.16 + "select phase, count(*) as total "+ 3.17 + "from lpit_issue " + 3.18 + "join lpit_issue_phases using(status) " + 3.19 + "where project = ? "+ 3.20 + "group by phase " 3.21 + ); 3.22 + 3.23 insert = connection.prepareStatement( 3.24 "insert into lpit_project (name, description, repourl, owner) values (?, ?, ?, ?)" 3.25 ); 3.26 @@ -89,6 +98,26 @@ 3.27 return proj; 3.28 } 3.29 3.30 + private void mapIssueSummary(Project proj) throws SQLException { 3.31 + issue_summary.setInt(1, proj.getId()); 3.32 + final var result = issue_summary.executeQuery(); 3.33 + while (result.next()) { 3.34 + final var phase = result.getInt("phase"); 3.35 + final var total = result.getInt("total"); 3.36 + switch(phase) { 3.37 + case 0: 3.38 + proj.setOpenIssues(total); 3.39 + break; 3.40 + case 1: 3.41 + proj.setActiveIssues(total); 3.42 + break; 3.43 + case 2: 3.44 + proj.setDoneIssues(total); 3.45 + break; 3.46 + } 3.47 + } 3.48 + } 3.49 + 3.50 @Override 3.51 public void save(Project instance) throws SQLException { 3.52 Objects.requireNonNull(instance.getName()); 3.53 @@ -116,7 +145,9 @@ 3.54 List<Project> projects = new ArrayList<>(); 3.55 try (var result = list.executeQuery()) { 3.56 while (result.next()) { 3.57 - projects.add(mapColumns(result)); 3.58 + final var project = mapColumns(result); 3.59 + mapIssueSummary(project); 3.60 + projects.add(project); 3.61 } 3.62 } 3.63 return projects; 3.64 @@ -127,7 +158,9 @@ 3.65 find.setInt(1, id); 3.66 try (var result = find.executeQuery()) { 3.67 if (result.next()) { 3.68 - return mapColumns(result); 3.69 + final var project = mapColumns(result); 3.70 + mapIssueSummary(project); 3.71 + return project; 3.72 } else { 3.73 return null; 3.74 }
4.1 --- a/src/main/java/de/uapcore/lightpit/entities/Issue.java Sun May 24 15:30:43 2020 +0200 4.2 +++ b/src/main/java/de/uapcore/lightpit/entities/Issue.java Sat May 30 15:26:15 2020 +0200 4.3 @@ -84,6 +84,10 @@ 4.4 this.status = status; 4.5 } 4.6 4.7 + public int getPhase() { 4.8 + return this.status.getPhase(); 4.9 + } 4.10 + 4.11 public IssueCategory getCategory() { 4.12 return category; 4.13 }
5.1 --- a/src/main/java/de/uapcore/lightpit/entities/IssueStatus.java Sun May 24 15:30:43 2020 +0200 5.2 +++ b/src/main/java/de/uapcore/lightpit/entities/IssueStatus.java Sat May 30 15:26:15 2020 +0200 5.3 @@ -39,6 +39,10 @@ 5.4 Withdrawn(2), 5.5 Duplicate(2); 5.6 5.7 + public static final int PHASE_OPEN = 0; 5.8 + public static final int PHASE_WIP = 1; 5.9 + public static final int PHASE_DONE = 2; 5.10 + 5.11 private int phase; 5.12 5.13 IssueStatus(int phase) {
6.1 --- a/src/main/java/de/uapcore/lightpit/entities/Project.java Sun May 24 15:30:43 2020 +0200 6.2 +++ b/src/main/java/de/uapcore/lightpit/entities/Project.java Sat May 30 15:26:15 2020 +0200 6.3 @@ -38,6 +38,10 @@ 6.4 private String repoUrl; 6.5 private User owner; 6.6 6.7 + private int openIssues; 6.8 + private int activeIssues; 6.9 + private int doneIssues; 6.10 + 6.11 public Project(int id) { 6.12 this.id = id; 6.13 } 6.14 @@ -78,6 +82,30 @@ 6.15 this.owner = owner; 6.16 } 6.17 6.18 + public int getOpenIssues() { 6.19 + return openIssues; 6.20 + } 6.21 + 6.22 + public void setOpenIssues(int openIssues) { 6.23 + this.openIssues = openIssues; 6.24 + } 6.25 + 6.26 + public int getActiveIssues() { 6.27 + return activeIssues; 6.28 + } 6.29 + 6.30 + public void setActiveIssues(int activeIssues) { 6.31 + this.activeIssues = activeIssues; 6.32 + } 6.33 + 6.34 + public int getDoneIssues() { 6.35 + return doneIssues; 6.36 + } 6.37 + 6.38 + public void setDoneIssues(int doneIssues) { 6.39 + this.doneIssues = doneIssues; 6.40 + } 6.41 + 6.42 @Override 6.43 public boolean equals(Object o) { 6.44 if (this == o) return true;
7.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sun May 24 15:30:43 2020 +0200 7.2 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat May 30 15:26:15 2020 +0200 7.3 @@ -303,6 +303,7 @@ 7.4 7.5 setAttributeHideZeros(req); 7.6 7.7 + req.setAttribute("project", sessionSelection.project); 7.8 req.setAttribute("versions", versions); 7.9 req.setAttribute("statsAffected", statsAffected); 7.10 req.setAttribute("statsScheduled", statsScheduled); 7.11 @@ -429,9 +430,8 @@ 7.12 getParameter(req, Date.class, "eta").ifPresent(issue::setEta); 7.13 dao.getIssueDao().saveOrUpdate(issue); 7.14 7.15 - // TODO: redirect to issue overview 7.16 // specifying the issue parameter keeps the edited issue as breadcrumb 7.17 - setRedirectLocation(req, "./projects/view?issue="+issue.getId()); 7.18 + setRedirectLocation(req, "./projects/issues/?issue="+issue.getId()); 7.19 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 7.20 LOG.debug("Successfully updated issue {} for project {}", issue.getId(), sessionSelection.project.getName()); 7.21 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
8.1 --- a/src/main/resources/localization/projects.properties Sun May 24 15:30:43 2020 +0200 8.2 +++ b/src/main/resources/localization/projects.properties Sat May 30 15:26:15 2020 +0200 8.3 @@ -39,6 +39,9 @@ 8.4 thead.description=Description 8.5 thead.repoUrl=Repository 8.6 thead.owner=Project Lead 8.7 +thead.issues.open=Open 8.8 +thead.issues.active=In Progress 8.9 +thead.issues.done=Done 8.10 8.11 thead.version.project=Project 8.12 thead.version.name=Version
9.1 --- a/src/main/resources/localization/projects_de.properties Sun May 24 15:30:43 2020 +0200 9.2 +++ b/src/main/resources/localization/projects_de.properties Sat May 30 15:26:15 2020 +0200 9.3 @@ -39,6 +39,9 @@ 9.4 thead.description=Beschreibung 9.5 thead.repoUrl=Repository 9.6 thead.owner=Projektleitung 9.7 +thead.issues.open=Offen 9.8 +thead.issues.active=In Arbeit 9.9 +thead.issues.done=Erledigt 9.10 9.11 thead.version.project=Projekt 9.12 thead.version.name=Version
10.1 --- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sun May 24 15:30:43 2020 +0200 10.2 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat May 30 15:26:15 2020 +0200 10.3 @@ -162,7 +162,7 @@ 10.4 <input type="hidden" name="id" value="${issue.id}"/> 10.5 <c:choose> 10.6 <c:when test="${not empty issue.project and issue.project.id ge 0}"> 10.7 - <c:set var="cancelUrl">./projects/view?pid=${issue.project.id}</c:set> 10.8 + <c:set var="cancelUrl">./projects/issues/?pid=${issue.project.id}</c:set> 10.9 </c:when> 10.10 <c:otherwise> 10.11 <c:set var="cancelUrl">./projects/</c:set>
11.1 --- a/src/main/webapp/WEB-INF/jsp/issues.jsp Sun May 24 15:30:43 2020 +0200 11.2 +++ b/src/main/webapp/WEB-INF/jsp/issues.jsp Sat May 30 15:26:15 2020 +0200 11.3 @@ -52,9 +52,11 @@ 11.4 <c:forEach var="issue" items="${issues}"> 11.5 <tr> 11.6 <td> 11.7 - <a href="./projects/issues/edit?id=${issue.id}"> 11.8 - <c:out value="${issue.subject}" /> 11.9 - </a> 11.10 + <span class="phase-${issue.status.phase}"> 11.11 + <a href="./projects/issues/edit?id=${issue.id}"> 11.12 + <c:out value="${issue.subject}" /> 11.13 + </a> 11.14 + </span> 11.15 </td> 11.16 <td> 11.17 <c:if test="${not empty issue.assignee}">
12.1 --- a/src/main/webapp/WEB-INF/jsp/project-details.jsp Sun May 24 15:30:43 2020 +0200 12.2 +++ b/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat May 30 15:26:15 2020 +0200 12.3 @@ -28,6 +28,7 @@ 12.4 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 12.5 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 12.6 12.7 +<jsp:useBean id="project" type="de.uapcore.lightpit.entities.Project" scope="request" /> 12.8 <jsp:useBean id="versions" type="java.util.List<de.uapcore.lightpit.entities.Version>" scope="request"/> 12.9 <jsp:useBean id="statsAffected" type="java.util.List<de.uapcore.lightpit.entities.VersionStatistics>" scope="request"/> 12.10 <jsp:useBean id="statsScheduled" type="java.util.List<de.uapcore.lightpit.entities.VersionStatistics>" scope="request"/> 12.11 @@ -36,6 +37,28 @@ 12.12 <jsp:useBean id="issueCategoryEnum" type="de.uapcore.lightpit.entities.IssueCategory[]" scope="request"/> 12.13 <jsp:useBean id="statsHideZeros" type="java.lang.Boolean" scope="request"/> 12.14 12.15 +<div id="project-attributes"> 12.16 + <div class="row"> 12.17 + <div class="caption"><fmt:message key="thead.name"/>:</div> 12.18 + <div><c:out value="${project.name}"/></div> 12.19 + <div class="caption"><fmt:message key="thead.description"/>:</div> 12.20 + <div><c:out value="${project.description}"/></div> 12.21 + </div> 12.22 + <div class="row"> 12.23 + <div class="caption"><fmt:message key="thead.owner"/>:</div> 12.24 + <div> 12.25 + <c:if test="${not empty project.owner}"><c:out value="${project.owner.displayname}"/></c:if> 12.26 + </div> 12.27 + <div class="caption"><fmt:message key="thead.repoUrl"/>:</div> 12.28 + <div> 12.29 + <c:if test="${not empty project.repoUrl}"> 12.30 + <a target="_blank" href="<c:out value="${project.repoUrl}"/>"><c:out 12.31 + value="${project.repoUrl}"/></a> 12.32 + </c:if> 12.33 + </div> 12.34 + </div> 12.35 +</div> 12.36 + 12.37 <div id="tool-area"> 12.38 <a href="./projects/versions/edit" class="button"><fmt:message key="button.version.create"/></a> 12.39 <a href="./projects/issues/edit" class="button"><fmt:message key="button.issue.create"/></a>
13.1 --- a/src/main/webapp/WEB-INF/jsp/project-form.jsp Sun May 24 15:30:43 2020 +0200 13.2 +++ b/src/main/webapp/WEB-INF/jsp/project-form.jsp Sat May 30 15:26:15 2020 +0200 13.3 @@ -43,7 +43,7 @@ 13.4 <td><input name="name" type="text" maxlength="20" required value="<c:out value="${project.name}"/>" /></td> 13.5 </tr> 13.6 <tr> 13.7 - <th class="vtop"><fmt:message key="thead.description"/></th> 13.8 + <th><fmt:message key="thead.description"/></th> 13.9 <td><input type="text" name="description" maxlength="200" value="<c:out value="${project.description}"/>" /></td> 13.10 </tr> 13.11 <tr>
14.1 --- a/src/main/webapp/WEB-INF/jsp/projects.jsp Sun May 24 15:30:43 2020 +0200 14.2 +++ b/src/main/webapp/WEB-INF/jsp/projects.jsp Sat May 30 15:26:15 2020 +0200 14.3 @@ -44,21 +44,23 @@ 14.4 </div> 14.5 14.6 <c:if test="${not empty projects}"> 14.7 - <table id="project-list" class="datatable medskip fullwidth"> 14.8 + <table id="project-list" class="datatable medskip"> 14.9 <colgroup> 14.10 <col> 14.11 - <col style="width: 10%"> 14.12 - <col style="width: 35%"> 14.13 - <col style="width: 30%"> 14.14 - <col style="width: 25%"> 14.15 + <col width="20%"> 14.16 + <col width="50%"> 14.17 + <col width="10%"> 14.18 + <col width="10%"> 14.19 + <col width="10%"> 14.20 </colgroup> 14.21 <thead> 14.22 <tr> 14.23 <th></th> 14.24 <th><fmt:message key="thead.name"/></th> 14.25 - <th><fmt:message key="thead.description"/></th> 14.26 <th><fmt:message key="thead.repoUrl"/></th> 14.27 - <th><fmt:message key="thead.owner"/></th> 14.28 + <th><fmt:message key="thead.issues.open"/></th> 14.29 + <th><fmt:message key="thead.issues.active"/></th> 14.30 + <th><fmt:message key="thead.issues.done"/></th> 14.31 </tr> 14.32 </thead> 14.33 <tbody> 14.34 @@ -67,17 +69,15 @@ 14.35 <td style="width: 2em;"><a href="./projects/edit?id=${project.id}">✎</a></td> 14.36 <td><a href="./projects/view?pid=${project.id}"><c:out value="${project.name}"/></a> 14.37 </td> 14.38 - <td><c:out value="${project.description}"/></td> 14.39 <td> 14.40 <c:if test="${not empty project.repoUrl}"> 14.41 <a target="_blank" href="<c:out value="${project.repoUrl}"/>"><c:out 14.42 value="${project.repoUrl}"/></a> 14.43 </c:if> 14.44 </td> 14.45 - <td> 14.46 - <c:if test="${not empty project.owner}"><c:out value="${project.owner.displayname}"/></c:if> 14.47 - <c:if test="${empty project.owner}"><fmt:message key="placeholder.null-owner"/></c:if> 14.48 - </td> 14.49 + <td>${project.openIssues}</td> 14.50 + <td>${project.activeIssues}</td> 14.51 + <td>${project.doneIssues}</td> 14.52 </tr> 14.53 </c:forEach> 14.54 </tbody>
15.1 --- a/src/main/webapp/projects.css Sun May 24 15:30:43 2020 +0200 15.2 +++ b/src/main/webapp/projects.css Sat May 30 15:26:15 2020 +0200 15.3 @@ -37,4 +37,29 @@ 15.4 15.5 #version-stats td { 15.6 text-align: right; 15.7 -} 15.8 \ No newline at end of file 15.9 +} 15.10 + 15.11 +#project-attributes { 15.12 + margin-bottom: 2em; 15.13 + display: table; 15.14 +} 15.15 + 15.16 +.row { 15.17 + display: table-row; 15.18 +} 15.19 + 15.20 +.caption { 15.21 + font-weight: bold; 15.22 +} 15.23 + 15.24 +.row > div { 15.25 + display: table-cell; 15.26 +} 15.27 + 15.28 +.row > div + div { 15.29 + padding-left: 2em; 15.30 +} 15.31 + 15.32 +span.phase-2 { 15.33 + text-decoration: line-through; 15.34 +}