adds issue summaries

Sat, 30 May 2020 15:26:15 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 30 May 2020 15:26:15 +0200
changeset 81
1a2e7b5d48f7
parent 80
27a25f32048e
child 82
4ec7f2600c83

adds issue summaries

setup/postgres/psql_create_tables.sql file | annotate | diff | comparison | revisions
setup/postgres/psql_default_data.sql file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/entities/Issue.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/entities/IssueStatus.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/entities/Project.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java file | annotate | diff | comparison | revisions
src/main/resources/localization/projects.properties file | annotate | diff | comparison | revisions
src/main/resources/localization/projects_de.properties file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/issue-form.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/issues.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/project-details.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/project-form.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/projects.jsp file | annotate | diff | comparison | revisions
src/main/webapp/projects.css file | annotate | diff | comparison | revisions
--- 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}">&#x270e;</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;
+}

mercurial