Sat, 30 May 2020 15:26:15 +0200
adds issue summaries
--- a/setup/postgres/psql_create_tables.sql Sun May 24 15:30:43 2020 +0200 +++ b/setup/postgres/psql_create_tables.sql Sat May 30 15:26:15 2020 +0200 @@ -41,7 +41,8 @@ 'InReview', 'Done', 'Rejected', - 'Withdrawn' + 'Withdrawn', + 'Duplicate' ); create type issue_category as enum ( @@ -52,6 +53,11 @@ 'Test' ); +create table lpit_issue_phases ( + status issue_status primary key, + phase integer not null +); + create table lpit_issue ( issueid serial primary key, project integer not null references lpit_project(projectid),
--- a/setup/postgres/psql_default_data.sql Sun May 24 15:30:43 2020 +0200 +++ b/setup/postgres/psql_default_data.sql Sat May 30 15:26:15 2020 +0200 @@ -0,0 +1,10 @@ +insert into lpit_issue_phases (status, phase) values + ('InSpecification', 0), + ('ToDo', 0), + ('Scheduled', 1), + ('InProgress', 1), + ('InReview', 1), + ('Done', 2), + ('Rejected', 2), + ('Withdrawn', 2), + ('Duplicate', 2);
--- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Sun May 24 15:30:43 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Sat May 30 15:26:15 2020 +0200 @@ -46,6 +46,7 @@ public final class PGProjectDao implements ProjectDao { private final PreparedStatement insert, update, list, find; + private final PreparedStatement issue_summary; public PGProjectDao(Connection connection) throws SQLException { list = connection.prepareStatement( @@ -62,6 +63,14 @@ "left join lpit_user owner on lpit_project.owner = owner.userid " + "where projectid = ?"); + issue_summary = connection.prepareStatement( + "select phase, count(*) as total "+ + "from lpit_issue " + + "join lpit_issue_phases using(status) " + + "where project = ? "+ + "group by phase " + ); + insert = connection.prepareStatement( "insert into lpit_project (name, description, repourl, owner) values (?, ?, ?, ?)" ); @@ -89,6 +98,26 @@ return proj; } + private void mapIssueSummary(Project proj) throws SQLException { + issue_summary.setInt(1, proj.getId()); + final var result = issue_summary.executeQuery(); + while (result.next()) { + final var phase = result.getInt("phase"); + final var total = result.getInt("total"); + switch(phase) { + case 0: + proj.setOpenIssues(total); + break; + case 1: + proj.setActiveIssues(total); + break; + case 2: + proj.setDoneIssues(total); + break; + } + } + } + @Override public void save(Project instance) throws SQLException { Objects.requireNonNull(instance.getName()); @@ -116,7 +145,9 @@ List<Project> projects = new ArrayList<>(); try (var result = list.executeQuery()) { while (result.next()) { - projects.add(mapColumns(result)); + final var project = mapColumns(result); + mapIssueSummary(project); + projects.add(project); } } return projects; @@ -127,7 +158,9 @@ find.setInt(1, id); try (var result = find.executeQuery()) { if (result.next()) { - return mapColumns(result); + final var project = mapColumns(result); + mapIssueSummary(project); + return project; } else { return null; }
--- a/src/main/java/de/uapcore/lightpit/entities/Issue.java Sun May 24 15:30:43 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Issue.java Sat May 30 15:26:15 2020 +0200 @@ -84,6 +84,10 @@ this.status = status; } + public int getPhase() { + return this.status.getPhase(); + } + public IssueCategory getCategory() { return category; }
--- a/src/main/java/de/uapcore/lightpit/entities/IssueStatus.java Sun May 24 15:30:43 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/IssueStatus.java Sat May 30 15:26:15 2020 +0200 @@ -39,6 +39,10 @@ Withdrawn(2), Duplicate(2); + public static final int PHASE_OPEN = 0; + public static final int PHASE_WIP = 1; + public static final int PHASE_DONE = 2; + private int phase; IssueStatus(int phase) {
--- a/src/main/java/de/uapcore/lightpit/entities/Project.java Sun May 24 15:30:43 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Project.java Sat May 30 15:26:15 2020 +0200 @@ -38,6 +38,10 @@ private String repoUrl; private User owner; + private int openIssues; + private int activeIssues; + private int doneIssues; + public Project(int id) { this.id = id; } @@ -78,6 +82,30 @@ this.owner = owner; } + public int getOpenIssues() { + return openIssues; + } + + public void setOpenIssues(int openIssues) { + this.openIssues = openIssues; + } + + public int getActiveIssues() { + return activeIssues; + } + + public void setActiveIssues(int activeIssues) { + this.activeIssues = activeIssues; + } + + public int getDoneIssues() { + return doneIssues; + } + + public void setDoneIssues(int doneIssues) { + this.doneIssues = doneIssues; + } + @Override public boolean equals(Object o) { if (this == o) return true;
--- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sun May 24 15:30:43 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat May 30 15:26:15 2020 +0200 @@ -303,6 +303,7 @@ setAttributeHideZeros(req); + req.setAttribute("project", sessionSelection.project); req.setAttribute("versions", versions); req.setAttribute("statsAffected", statsAffected); req.setAttribute("statsScheduled", statsScheduled); @@ -429,9 +430,8 @@ getParameter(req, Date.class, "eta").ifPresent(issue::setEta); dao.getIssueDao().saveOrUpdate(issue); - // TODO: redirect to issue overview // specifying the issue parameter keeps the edited issue as breadcrumb - setRedirectLocation(req, "./projects/view?issue="+issue.getId()); + setRedirectLocation(req, "./projects/issues/?issue="+issue.getId()); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); LOG.debug("Successfully updated issue {} for project {}", issue.getId(), sessionSelection.project.getName()); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
--- a/src/main/resources/localization/projects.properties Sun May 24 15:30:43 2020 +0200 +++ b/src/main/resources/localization/projects.properties Sat May 30 15:26:15 2020 +0200 @@ -39,6 +39,9 @@ thead.description=Description thead.repoUrl=Repository thead.owner=Project Lead +thead.issues.open=Open +thead.issues.active=In Progress +thead.issues.done=Done thead.version.project=Project thead.version.name=Version
--- a/src/main/resources/localization/projects_de.properties Sun May 24 15:30:43 2020 +0200 +++ b/src/main/resources/localization/projects_de.properties Sat May 30 15:26:15 2020 +0200 @@ -39,6 +39,9 @@ thead.description=Beschreibung thead.repoUrl=Repository thead.owner=Projektleitung +thead.issues.open=Offen +thead.issues.active=In Arbeit +thead.issues.done=Erledigt thead.version.project=Projekt thead.version.name=Version
--- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sun May 24 15:30:43 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat May 30 15:26:15 2020 +0200 @@ -162,7 +162,7 @@ <input type="hidden" name="id" value="${issue.id}"/> <c:choose> <c:when test="${not empty issue.project and issue.project.id ge 0}"> - <c:set var="cancelUrl">./projects/view?pid=${issue.project.id}</c:set> + <c:set var="cancelUrl">./projects/issues/?pid=${issue.project.id}</c:set> </c:when> <c:otherwise> <c:set var="cancelUrl">./projects/</c:set>
--- a/src/main/webapp/WEB-INF/jsp/issues.jsp Sun May 24 15:30:43 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/issues.jsp Sat May 30 15:26:15 2020 +0200 @@ -52,9 +52,11 @@ <c:forEach var="issue" items="${issues}"> <tr> <td> - <a href="./projects/issues/edit?id=${issue.id}"> - <c:out value="${issue.subject}" /> - </a> + <span class="phase-${issue.status.phase}"> + <a href="./projects/issues/edit?id=${issue.id}"> + <c:out value="${issue.subject}" /> + </a> + </span> </td> <td> <c:if test="${not empty issue.assignee}">
--- a/src/main/webapp/WEB-INF/jsp/project-details.jsp Sun May 24 15:30:43 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat May 30 15:26:15 2020 +0200 @@ -28,6 +28,7 @@ <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<jsp:useBean id="project" type="de.uapcore.lightpit.entities.Project" scope="request" /> <jsp:useBean id="versions" type="java.util.List<de.uapcore.lightpit.entities.Version>" scope="request"/> <jsp:useBean id="statsAffected" type="java.util.List<de.uapcore.lightpit.entities.VersionStatistics>" scope="request"/> <jsp:useBean id="statsScheduled" type="java.util.List<de.uapcore.lightpit.entities.VersionStatistics>" scope="request"/> @@ -36,6 +37,28 @@ <jsp:useBean id="issueCategoryEnum" type="de.uapcore.lightpit.entities.IssueCategory[]" scope="request"/> <jsp:useBean id="statsHideZeros" type="java.lang.Boolean" scope="request"/> +<div id="project-attributes"> + <div class="row"> + <div class="caption"><fmt:message key="thead.name"/>:</div> + <div><c:out value="${project.name}"/></div> + <div class="caption"><fmt:message key="thead.description"/>:</div> + <div><c:out value="${project.description}"/></div> + </div> + <div class="row"> + <div class="caption"><fmt:message key="thead.owner"/>:</div> + <div> + <c:if test="${not empty project.owner}"><c:out value="${project.owner.displayname}"/></c:if> + </div> + <div class="caption"><fmt:message key="thead.repoUrl"/>:</div> + <div> + <c:if test="${not empty project.repoUrl}"> + <a target="_blank" href="<c:out value="${project.repoUrl}"/>"><c:out + value="${project.repoUrl}"/></a> + </c:if> + </div> + </div> +</div> + <div id="tool-area"> <a href="./projects/versions/edit" class="button"><fmt:message key="button.version.create"/></a> <a href="./projects/issues/edit" class="button"><fmt:message key="button.issue.create"/></a>
--- a/src/main/webapp/WEB-INF/jsp/project-form.jsp Sun May 24 15:30:43 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/project-form.jsp Sat May 30 15:26:15 2020 +0200 @@ -43,7 +43,7 @@ <td><input name="name" type="text" maxlength="20" required value="<c:out value="${project.name}"/>" /></td> </tr> <tr> - <th class="vtop"><fmt:message key="thead.description"/></th> + <th><fmt:message key="thead.description"/></th> <td><input type="text" name="description" maxlength="200" value="<c:out value="${project.description}"/>" /></td> </tr> <tr>
--- a/src/main/webapp/WEB-INF/jsp/projects.jsp Sun May 24 15:30:43 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/projects.jsp Sat May 30 15:26:15 2020 +0200 @@ -44,21 +44,23 @@ </div> <c:if test="${not empty projects}"> - <table id="project-list" class="datatable medskip fullwidth"> + <table id="project-list" class="datatable medskip"> <colgroup> <col> - <col style="width: 10%"> - <col style="width: 35%"> - <col style="width: 30%"> - <col style="width: 25%"> + <col width="20%"> + <col width="50%"> + <col width="10%"> + <col width="10%"> + <col width="10%"> </colgroup> <thead> <tr> <th></th> <th><fmt:message key="thead.name"/></th> - <th><fmt:message key="thead.description"/></th> <th><fmt:message key="thead.repoUrl"/></th> - <th><fmt:message key="thead.owner"/></th> + <th><fmt:message key="thead.issues.open"/></th> + <th><fmt:message key="thead.issues.active"/></th> + <th><fmt:message key="thead.issues.done"/></th> </tr> </thead> <tbody> @@ -67,17 +69,15 @@ <td style="width: 2em;"><a href="./projects/edit?id=${project.id}">✎</a></td> <td><a href="./projects/view?pid=${project.id}"><c:out value="${project.name}"/></a> </td> - <td><c:out value="${project.description}"/></td> <td> <c:if test="${not empty project.repoUrl}"> <a target="_blank" href="<c:out value="${project.repoUrl}"/>"><c:out value="${project.repoUrl}"/></a> </c:if> </td> - <td> - <c:if test="${not empty project.owner}"><c:out value="${project.owner.displayname}"/></c:if> - <c:if test="${empty project.owner}"><fmt:message key="placeholder.null-owner"/></c:if> - </td> + <td>${project.openIssues}</td> + <td>${project.activeIssues}</td> + <td>${project.doneIssues}</td> </tr> </c:forEach> </tbody>
--- a/src/main/webapp/projects.css Sun May 24 15:30:43 2020 +0200 +++ b/src/main/webapp/projects.css Sat May 30 15:26:15 2020 +0200 @@ -37,4 +37,29 @@ #version-stats td { text-align: right; -} \ No newline at end of file +} + +#project-attributes { + margin-bottom: 2em; + display: table; +} + +.row { + display: table-row; +} + +.caption { + font-weight: bold; +} + +.row > div { + display: table-cell; +} + +.row > div + div { + padding-left: 2em; +} + +span.phase-2 { + text-decoration: line-through; +}