[Notice] Announcing the End of Demo Server [Read me]
Yi EungJun 2013-01-23
milestone: Calculate numbers just in time.
Milestone has stored many calcaulted values (e.g. number of open issues)
persistently in database and it causes many problems. They should be
calcultaed just in time.

Also remove many lines of codes because this fix eliminates the needs.
@57b8cc7b98a8eb4d620bdcc1ff41442cecd63e7a
app/controllers/MilestoneApp.java
--- app/controllers/MilestoneApp.java
+++ app/controllers/MilestoneApp.java
@@ -38,9 +38,6 @@
                 mCondition.sort,
                 Direction.getValue(mCondition.direction));
 
-        for(Milestone milestone: milestones) {
-            milestone.updateIssueInfo();
-        }
         return ok(list.render("title.milestoneList", milestones, project, mCondition));
     }
 
@@ -105,7 +102,6 @@
         } else {
             Milestone existingMilestone = Milestone.findById(milestoneId);
             existingMilestone.updateWith(milestoneForm.get());
-            Milestone.update(existingMilestone, milestoneId);
             return redirect(routes.MilestoneApp.manageMilestones(userName, projectName));
         }
     }
app/models/Issue.java
--- app/models/Issue.java
+++ app/models/Issue.java
@@ -196,10 +196,6 @@
      */
     public static Long create(Issue issue) {
         issue.save();
-        if (issue.milestoneId != null) {
-            Milestone milestone = Milestone.findById(issue.milestoneId);
-            milestone.add(issue);
-        }
         return issue.id;
     }
 
@@ -210,10 +206,6 @@
      */
     public static void delete(Long id) {
         Issue issue = finder.byId(id);
-        if (issue.milestoneId != null && !issue.milestoneId.equals(0l)) {
-            Milestone milestone = Milestone.findById(issue.milestoneId);
-            milestone.delete(issue);
-        }
         issue.delete();
     }
 
app/models/Milestone.java
--- app/models/Milestone.java
+++ app/models/Milestone.java
@@ -35,11 +35,6 @@
     @Constraints.Required
     public State state;
 
-    public int numClosedIssues;
-    public int numOpenIssues;
-    public int numTotalIssues;
-    public int completionRate;
-
     @ManyToOne
     public Project project;
 
@@ -47,18 +42,20 @@
         milestone.save();
     }
 
-    public static void update(Milestone milestone, Long id) {
-        int completionRate = 0;
-        if (milestone.numTotalIssues != 0) {
-            completionRate =
-                    calculateCompletionRate(milestone.numTotalIssues, milestone.numClosedIssues);
-        }
-        milestone.completionRate = completionRate;
-        milestone.update(id);
+    public int getNumClosedIssues() {
+        return Issue.finder.where().eq("milestoneId", this.id).eq("state", State.CLOSED).findRowCount();
     }
 
-    private static int calculateCompletionRate(int numTotalIssues, int numClosedIssues) {
-        return new Double(((double) numClosedIssues / (double) numTotalIssues) * 100).intValue();
+    public int getNumOpenIssues() {
+        return Issue.finder.where().eq("milestoneId", this.id).eq("state", State.OPEN).findRowCount();
+    }
+
+    public int getNumTotalIssues() {
+        return Issue.findByMilestoneId(this.id).size();
+    }
+
+    public int getCompletionRate() {
+        return new Double(((double) getNumClosedIssues() / (double) getNumTotalIssues()) * 100).intValue();
     }
 
     public static void delete(Milestone milestone) {
@@ -162,55 +159,5 @@
             options.put(milestone.id.toString(), milestone.title);
         }
         return options;
-    }
-
-    public void add(Issue issue) {
-        findIssuesNUpdateTotalCount();
-        checkIssueState(issue, 1);
-        this._save();
-    }
-
-    public void updateIssueInfo() {
-        List<Issue> issues = findIssuesNUpdateTotalCount();
-
-        this.numOpenIssues = 0;
-        this.numClosedIssues = 0;
-
-        for (Issue issue : issues) {
-            checkIssueState(issue, 1);
-        }
-
-        this.completionRate = calculateCompletionRate(this.numTotalIssues, this.numClosedIssues);
-        update(this.id);
-    }
-
-    public void delete(Issue issue) {
-        this.numTotalIssues -= 1;
-        checkIssueState(issue, -1);
-        this._save();
-    }
-
-    private void checkIssueState(Issue issue, int count) {
-        if (issue.isOpen()) {
-            this.numOpenIssues += count;
-        } else {
-            this.numClosedIssues += count;
-        }
-    }
-
-    private void _save() {
-        this.completionRate = calculateCompletionRate(this.numTotalIssues, this.numClosedIssues);
-        this.save();
-    }
-
-    /**
-     * 마일스톤에 연결된 이슈들을 찾아주며 해당 마일스톤에 할당된 이슈의 총 갯수를 update 해준다.
-     *
-     * @return
-     */
-    private List<Issue> findIssuesNUpdateTotalCount() {
-        List<Issue> issues = Issue.findByMilestoneId(this.id);
-        this.numTotalIssues = issues.size();
-        return issues;
     }
 }
app/views/milestone/list.scala.html
--- app/views/milestone/list.scala.html
+++ app/views/milestone/list.scala.html
@@ -84,20 +84,20 @@
                 </div>
                 <div class="progress-wrap">
                     <div class="progress">
-                        <div class="bar orange" style="width: @milestone.completionRate%;"></div>
+                        <div class="bar orange" style="width: @milestone.getCompletionRate%;"></div>
                     </div>
                     <div class="pull-left">
-                        <a href="@makeIssuesLink(milestone.id,"closed")">완료 @milestone.numClosedIssues (@milestone.completionRate%)</a>
+                        <a href="@makeIssuesLink(milestone.id,"closed")">완료 @milestone.getNumClosedIssues (@milestone.getCompletionRate%)</a>
                     </div>
                     <div class="pull-right">
-                        <a href="@makeIssuesLink(milestone.id,"open")">미완료 @milestone.numOpenIssues (@{100 - milestone.completionRate}%)</a>
+                        <a href="@makeIssuesLink(milestone.id,"open")">미완료 @milestone.getNumOpenIssues (@{100 - milestone.getCompletionRate}%)</a>
                     </div>
                 </div>
                 <div class="desc">
                     @milestone.contents
                 </div>
             </div>
-            <div class="completion-rate @if(milestone.completionRate == 100){done}">@milestone.completionRate%</div>
+            <div class="completion-rate @if(milestone.getCompletionRate == 100){done}">@milestone.getCompletionRate%</div>
         </li>
         }
     </ul>
conf/initial-data.yml
--- conf/initial-data.yml
+++ conf/initial-data.yml
@@ -293,10 +293,6 @@
         dueDate:         2012-07-12 10:59:59
         contents:        nFORGE 첫번째 버전.
         state:      OPEN
-        numClosedIssues: 2
-        numOpenIssues:   2
-        numTotalIssues:  4
-        completionRate:  50
         project:         !!models.Project
                              id: 1
     - !!models.Milestone
@@ -304,10 +300,6 @@
         dueDate:         2012-07-19 10:59:59
         contents:        nFORGE 두번째 버전 (Stable).
         state:      OPEN
-        numClosedIssues: 2
-        numOpenIssues:   1
-        numTotalIssues:  3
-        completionRate:  66
         project:         !!models.Project
                              id: 1
     - !!models.Milestone
@@ -315,10 +307,6 @@
         dueDate:         2011-08-10 10:59:59
         contents:        Jindo 안정화 버전
         state:      CLOSED
-        numClosedIssues: 2
-        numOpenIssues:   0
-        numTotalIssues:  2
-        completionRate:  100
         project:         !!models.Project
                              id: 2
     - !!models.Milestone
@@ -326,10 +314,6 @@
         dueDate:         2012-04-11 10:59:59
         contents:        Jindo2
         state:      OPEN
-        numClosedIssues: 0
-        numOpenIssues:   3
-        numTotalIssues:  3
-        completionRate:  0
         project:         !!models.Project
                              id: 2
     - !!models.Milestone
@@ -337,10 +321,6 @@
         dueDate:         2012-12-31 10:59:59
         contents:        CUBRID v.0.1
         state:      OPEN
-        numClosedIssues: 1
-        numOpenIssues:   1
-        numTotalIssues:  2
-        completionRate:  50
         project:         !!models.Project
                              id: 3
     - !!models.Milestone
@@ -348,10 +328,6 @@
         dueDate:         2013-11-09 10:59:59
         contents:        CUBRID v.0.2
         state:      CLOSED
-        numClosedIssues: 1
-        numOpenIssues:   0
-        numTotalIssues:  1
-        completionRate:  100
         project:         !!models.Project
                              id: 3
 
test/models/MilestoneTest.java
--- test/models/MilestoneTest.java
+++ test/models/MilestoneTest.java
@@ -18,13 +18,11 @@
         newMilestone.dueDate = new Date();
         newMilestone.contents = "테스트 마일스톤";
         newMilestone.project = Project.find.byId(1l);
-        newMilestone.numClosedIssues = 0;
-        newMilestone.numOpenIssues = 0;
-        newMilestone.numTotalIssues = 0;
         newMilestone.title = "0.1";
-        newMilestone.completionRate = 0;
+
         // When
         Milestone.create(newMilestone);
+
         // Then
         assertThat(newMilestone.id).isNotNull();
     }
@@ -51,11 +49,11 @@
         assertThat(expactDueDate.get(Calendar.DAY_OF_MONTH)).isEqualTo(
             dueDate.get(Calendar.DAY_OF_MONTH));
 
-        assertThat(firstMilestone.numClosedIssues).isEqualTo(2);
-        assertThat(firstMilestone.numOpenIssues).isEqualTo(2);
-        assertThat(firstMilestone.numTotalIssues).isEqualTo(4);
+        assertThat(firstMilestone.getNumClosedIssues()).isEqualTo(2);
+        assertThat(firstMilestone.getNumOpenIssues()).isEqualTo(2);
+        assertThat(firstMilestone.getNumTotalIssues()).isEqualTo(4);
         assertThat(firstMilestone.project).isEqualTo(Project.find.byId(1l));
-        assertThat(firstMilestone.completionRate).isEqualTo(50);
+        assertThat(firstMilestone.getCompletionRate()).isEqualTo(50);
     }
 
     @Test
@@ -63,6 +61,7 @@
         // Given
         Milestone firstMilestone = Milestone.findById(1l);
         assertThat(firstMilestone).isNotNull();
+
         // When
         Milestone.delete(firstMilestone);
         flush();
@@ -70,29 +69,6 @@
         //Then
         firstMilestone = Milestone.findById(1l);
         assertThat(firstMilestone).isNull();
-    }
-
-    @Test
-    public void update() throws Exception {
-        // Given
-        Milestone updateMilestone = new Milestone();
-        updateMilestone.contents = "엔포지 첫번째 버전.";
-        updateMilestone.title = "1.0.0-SNAPSHOT";
-        updateMilestone.numClosedIssues = 10;
-        updateMilestone.numOpenIssues = 1;
-        updateMilestone.numTotalIssues = 11;
-
-        // When
-        Milestone.update(updateMilestone, 2l);
-        // Then
-        Milestone actualMilestone = Milestone.findById(2l);
-        assertThat(actualMilestone.contents)
-            .isEqualTo(updateMilestone.contents);
-        assertThat(actualMilestone.title).isEqualTo(updateMilestone.title);
-        assertThat(actualMilestone.numClosedIssues).isEqualTo(10);
-        assertThat(actualMilestone.numOpenIssues).isEqualTo(1);
-        assertThat(actualMilestone.numTotalIssues).isEqualTo(11);
-        assertThat(actualMilestone.completionRate).isEqualTo(90);
     }
 
     @Test
@@ -174,21 +150,6 @@
             State.ALL);
         // Then
         assertThat(p2Milestones.size()).isEqualTo(2);
-
-        // Given
-        // When
-        List<Milestone> p1MilestonesASCDirection = Milestone.findMilestones(1l,
-            State.ALL, "completionRate", Direction.ASC);
-        // Then
-        assertThat(p1MilestonesASCDirection.get(0).completionRate).isEqualTo(50);
-
-        // Given
-        // When
-        List<Milestone> p2MilestonesDESCDirection = Milestone.findMilestones(
-            2l, State.ALL, "completionRate", Direction.DESC);
-        // Then
-        assertThat(p2MilestonesDESCDirection.get(0).completionRate).isEqualTo(
-            100);
     }
 
     @Test
@@ -221,8 +182,8 @@
     public void addIssue() throws Exception {
         // GIVEN
         Milestone m5 = Milestone.findById(5l);
-        int totalNumber = m5.numTotalIssues;
-        int openNumber = m5.numOpenIssues;
+        int totalNumber = m5.getNumTotalIssues();
+        int openNumber = m5.getNumOpenIssues();
         Issue issue = new Issue("불필요한 로그 출력 코드 제거test");
         issue.date = JodaDateUtil.today();
         issue.state = State.OPEN;
@@ -234,8 +195,8 @@
 
         // THEN
         m5 = Milestone.findById(5l);
-        assertThat(m5.numTotalIssues).isEqualTo(totalNumber + 1);
-        assertThat(m5.numOpenIssues).isEqualTo(openNumber + 1);
+        assertThat(m5.getNumTotalIssues()).isEqualTo(totalNumber + 1);
+        assertThat(m5.getNumOpenIssues()).isEqualTo(openNumber + 1);
     }
 
     @Test
@@ -243,25 +204,23 @@
         //Given
         Issue issue = new Issue("불필요한 로그 출력 코드 제거test");
         issue.milestoneId = 6l;
-        issue.update(5l);
-
         Long milestoneId = issue.milestoneId;
         Milestone m6 = Milestone.findById(milestoneId);
-        assertThat(m6.numOpenIssues).isEqualTo(0);
-        assertThat(m6.numClosedIssues).isEqualTo(1);
-        assertThat(m6.numTotalIssues).isEqualTo(1);
-        assertThat(m6.completionRate).isEqualTo(100);
+        assertThat(m6.getNumOpenIssues()).isEqualTo(0);
+        assertThat(m6.getNumClosedIssues()).isEqualTo(1);
+        assertThat(m6.getNumTotalIssues()).isEqualTo(1);
+        assertThat(m6.getCompletionRate()).isEqualTo(100);
 
         //When
-        m6.updateIssueInfo();
+        issue.update(5l);
         flush();
 
         //Then
         m6 = Milestone.findById(milestoneId);
-        assertThat(m6.numOpenIssues).isEqualTo(1);
-        assertThat(m6.numClosedIssues).isEqualTo(1);
-        assertThat(m6.numTotalIssues).isEqualTo(2);
-        assertThat(m6.completionRate).isEqualTo(50);
+        assertThat(m6.getNumOpenIssues()).isEqualTo(1);
+        assertThat(m6.getNumClosedIssues()).isEqualTo(1);
+        assertThat(m6.getNumTotalIssues()).isEqualTo(2);
+        assertThat(m6.getCompletionRate()).isEqualTo(50);
     }
 
     @Test
@@ -269,19 +228,18 @@
         //Given
         Issue issue = Issue.findById(7l);
         Milestone m5 = Milestone.findById(issue.milestoneId);
-        assertThat(m5.numOpenIssues).isEqualTo(1);
-        assertThat(m5.numClosedIssues).isEqualTo(1);
-        assertThat(m5.numTotalIssues).isEqualTo(2);
-        assertThat(m5.completionRate).isEqualTo(50);
+        assertThat(m5.getNumOpenIssues()).isEqualTo(1);
+        assertThat(m5.getNumClosedIssues()).isEqualTo(1);
+        assertThat(m5.getNumTotalIssues()).isEqualTo(2);
+        assertThat(m5.getCompletionRate()).isEqualTo(50);
 
         //When
         issue.delete();
-        m5.delete(issue);
 
         //Then
-        assertThat(m5.numOpenIssues).isEqualTo(0);
-        assertThat(m5.numClosedIssues).isEqualTo(1);
-        assertThat(m5.numTotalIssues).isEqualTo(1);
-        assertThat(m5.completionRate).isEqualTo(100);
+        assertThat(m5.getNumOpenIssues()).isEqualTo(0);
+        assertThat(m5.getNumClosedIssues()).isEqualTo(1);
+        assertThat(m5.getNumTotalIssues()).isEqualTo(1);
+        assertThat(m5.getCompletionRate()).isEqualTo(100);
     }
-}
+}
(No newline at end of file)
Add a comment
List