From efd81930091414fcba977e2cebb9dd5560bd6e2e Mon Sep 17 00:00:00 2001 From: "Christian P. MOMON" Date: Wed, 22 Jan 2020 10:39:44 +0100 Subject: [PATCH] Added created/concluded X months chart. Normalized dates. --- .../agirstatool/charts/DateCountList.java | 42 ++++++ .../april/agirstatool/core/AgirStatool.java | 111 +++++++-------- .../agirstatool/core/AgirStatoolUtils.java | 127 ++++++++++++++++++ .../april/agirstatool/core/IssueStats.java | 10 +- src/org/april/agirstatool/core/Project.java | 33 +++++ .../april/agirstatool/core/ProjectMode.java | 28 ---- .../pages/CreatedConcludedCountChartView.java | 49 +++---- .../agirstatool/core/pages/ProjectPage.java | 2 + .../agirstatool/core/pages/project.xhtml | 2 + 9 files changed, 286 insertions(+), 118 deletions(-) delete mode 100644 src/org/april/agirstatool/core/ProjectMode.java diff --git a/src/org/april/agirstatool/charts/DateCountList.java b/src/org/april/agirstatool/charts/DateCountList.java index 92ec357..c78ef6e 100644 --- a/src/org/april/agirstatool/charts/DateCountList.java +++ b/src/org/april/agirstatool/charts/DateCountList.java @@ -20,6 +20,8 @@ package org.april.agirstatool.charts; import java.util.ArrayList; +import org.apache.commons.lang3.StringUtils; + import fr.devinsy.strings.StringList; /** @@ -37,6 +39,46 @@ public class DateCountList extends ArrayList super(); } + /** + * Indexof. + * + * @param dateToken + * the date token + * @return the int + */ + public int indexOf(final String dateToken) + { + int result; + + boolean ended = false; + result = -1; + int index = 0; + while (!ended) + { + if (index < this.size()) + { + DateCount current = get(index); + if (StringUtils.equals(current.getDate(), dateToken)) + { + ended = true; + result = index; + } + else + { + index += 1; + } + } + else + { + ended = true; + result = -1; + } + } + + // + return result; + } + /** * To value list. * diff --git a/src/org/april/agirstatool/core/AgirStatool.java b/src/org/april/agirstatool/core/AgirStatool.java index cbed5d9..c62c6a6 100644 --- a/src/org/april/agirstatool/core/AgirStatool.java +++ b/src/org/april/agirstatool/core/AgirStatool.java @@ -25,8 +25,6 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import org.apache.commons.io.FileUtils; import org.april.agirstatool.charts.DateCount; @@ -209,9 +207,12 @@ public class AgirStatool for (Project subProject : project.subProjects()) { refreshPage(subProject); + if (project.getName().equals("Chapril")) + { + System.exit(0); + } } } - } catch (IOException exception) { @@ -232,7 +233,7 @@ public class AgirStatool * @throws AgirStatoolException * the agir statool exception */ - public DateCountMap fetchWeekConcludedCount(final Project project, final ProjectMode mode) throws AgirStatoolException + public DateCountMap fetchWeekConcludedCount(final Project project) throws AgirStatoolException { DateCountMap result; @@ -244,7 +245,7 @@ public class AgirStatool try { StringList subSql = new StringList(); - if (mode == ProjectMode.CONSOLIDATED) + if (project.getType() == Project.Type.CONSOLIDATED) { subSql.append("select "); subSql.append(" id "); @@ -254,6 +255,15 @@ public class AgirStatool subSql.append(" (childProject.id=" + project.getId() + " or childProject.parent_id=" + project.getId() + ")"); subSql.append(" and childProject.status=1 and childProject.is_public=1"); } + else if (project.getType() == Project.Type.ROOT) + { + subSql.append("select "); + subSql.append(" id "); + subSql.append("from "); + subSql.append(" projects as childProject "); + subSql.append("where "); + subSql.append(" childProject.status=1 and childProject.is_public=1"); + } else { subSql.append(project.getId()); @@ -261,7 +271,7 @@ public class AgirStatool StringList sql = new StringList(); sql.append("SELECT"); - sql.append(" concat_ws('-', year(closed_on), weekofyear(closed_on)) as date, "); + sql.append(" concat_ws('-', year(closed_on), month(closed_on), weekofyear(closed_on)) as date, "); sql.append(" count(*) as count "); sql.append("FROM "); sql.append(" issues "); @@ -305,7 +315,7 @@ public class AgirStatool * @throws AgirStatoolException * the agir statool exception */ - public DateCountMap fetchWeekCreatedCount(final Project project, final ProjectMode mode) throws AgirStatoolException + public DateCountMap fetchWeekCreatedCount(final Project project) throws AgirStatoolException { DateCountMap result; @@ -317,7 +327,7 @@ public class AgirStatool try { StringList subSql = new StringList(); - if (mode == ProjectMode.CONSOLIDATED) + if (project.getType() == Project.Type.CONSOLIDATED) { subSql.append("select "); subSql.append(" id "); @@ -327,6 +337,15 @@ public class AgirStatool subSql.append(" (childProject.id=" + project.getId() + " or childProject.parent_id=" + project.getId() + ")"); subSql.append(" and childProject.status=1 and childProject.is_public=1"); } + else if (project.getType() == Project.Type.ROOT) + { + subSql.append("select "); + subSql.append(" id "); + subSql.append("from "); + subSql.append(" projects as childProject "); + subSql.append("where "); + subSql.append(" childProject.status=1 and childProject.is_public=1"); + } else { subSql.append(project.getId()); @@ -334,7 +353,7 @@ public class AgirStatool StringList sql = new StringList(); sql.append("SELECT "); - sql.append(" concat_ws('-', year(created_on), weekofyear(created_on)) as date, "); + sql.append(" concat_ws('-', year(created_on), month(created_on), weekofyear(created_on)) as date, "); sql.append(" count(*) as count "); sql.append("FROM "); sql.append(" issues "); @@ -557,30 +576,32 @@ public class AgirStatool } // Fill created and concluded issues history. + { + { + DateCountMap map = fetchWeekCreatedCount(result); + DateCountList counts = AgirStatoolUtils.normalizedWeekCountList(map, result.issueStats().getFirstCreate().toLocalDate()); + result.issueStats().setWeekCreatedIssueCounts(counts); + } + { + DateCountMap map = fetchWeekConcludedCount(result); + DateCountList counts = AgirStatoolUtils.normalizedWeekCountList(map, result.issueStats().getFirstCreate().toLocalDate()); + result.issueStats().setWeekConcludedIssueCounts(counts); + } + } for (Project project : projects) { logger.info("Fetching Created/Closed history for " + project.getName()); if (project.hasIssue()) { - ProjectMode mode; - if (project.getName().startsWith("@")) { - mode = ProjectMode.ALONE; - } - else - { - mode = ProjectMode.CONSOLIDATED; - } - - { - DateCountMap map = fetchWeekCreatedCount(project, mode); - DateCountList counts = normalizedWeekCountList(map, project.issueStats().getFirstCreate().toLocalDate()); + DateCountMap map = fetchWeekCreatedCount(project); + DateCountList counts = AgirStatoolUtils.normalizedWeekCountList(map, project.issueStats().getFirstCreate().toLocalDate()); project.issueStats().setWeekCreatedIssueCounts(counts); } { - DateCountMap map = fetchWeekConcludedCount(project, mode); - DateCountList counts = normalizedWeekCountList(map, project.issueStats().getFirstCreate().toLocalDate()); - project.issueStats().setWeekClosedIssueCounts(counts); + DateCountMap map = fetchWeekConcludedCount(project); + DateCountList counts = AgirStatoolUtils.normalizedWeekCountList(map, project.issueStats().getFirstCreate().toLocalDate()); + project.issueStats().setWeekConcludedIssueCounts(counts); } } } @@ -616,7 +637,7 @@ public class AgirStatool { Projects result; - result = listProjectsWithStats(ProjectMode.ALONE); + result = listProjectsWithStats(Project.Type.ALONE); // return result; } @@ -630,7 +651,7 @@ public class AgirStatool * @throws AgirStatoolException * the agir statool exception */ - public Projects listProjectsWithStats(final ProjectMode mode) throws AgirStatoolException + public Projects listProjectsWithStats(final Project.Type type) throws AgirStatoolException { Projects result; @@ -642,7 +663,7 @@ public class AgirStatool try { StringList subSql = new StringList(); - if (mode == ProjectMode.CONSOLIDATED) + if (type == Project.Type.CONSOLIDATED) { subSql.append("select "); subSql.append(" id "); @@ -768,41 +789,7 @@ public class AgirStatool { Projects result; - result = listProjectsWithStats(ProjectMode.CONSOLIDATED); - - // - return result; - } - - /** - * Builds the week created count list. - * - * @param source - * the source - * @param start - * the start - * @return the date count list - */ - public DateCountList normalizedWeekCountList(final DateCountMap source, final LocalDate start) - { - DateCountList result; - - result = new DateCountList(); - - LocalDate end = LocalDate.now(); - LocalDate date = start; - long count = 0; - while (date.isBefore(end) || date.isEqual(end)) - { - String dateToken = date.format(DateTimeFormatter.ofPattern("yyyy-w")); - DateCount current = source.get(dateToken); - if (current != null) - { - count += current.getCount(); - } - result.add(new DateCount(dateToken, count)); - date = date.plusDays(7); - } + result = listProjectsWithStats(Project.Type.CONSOLIDATED); // return result; diff --git a/src/org/april/agirstatool/core/AgirStatoolUtils.java b/src/org/april/agirstatool/core/AgirStatoolUtils.java index 2ab54f9..b3a37b5 100644 --- a/src/org/april/agirstatool/core/AgirStatoolUtils.java +++ b/src/org/april/agirstatool/core/AgirStatoolUtils.java @@ -18,12 +18,20 @@ */ package org.april.agirstatool.core; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.Date; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; +import org.april.agirstatool.charts.DateCount; +import org.april.agirstatool.charts.DateCountList; +import org.april.agirstatool.charts.DateCountMap; +import org.april.agirstatool.core.pages.ProjectPage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import fr.devinsy.strings.StringList; import fr.devinsy.strings.StringsUtils; @@ -33,9 +41,128 @@ import fr.devinsy.strings.StringsUtils; */ public class AgirStatoolUtils { + private static Logger logger = LoggerFactory.getLogger(ProjectPage.class); + public static final DateTimeFormatter PATTERN_SHORTDATE = DateTimeFormatter.ofPattern("dd/MM/yyyy"); public static final DateTimeFormatter PATTERN_LONGDATE = DateTimeFormatter.ofPattern("dd/MM/yyyy hh'h'mm"); + /** + * Normalise week date. + * + * @param source + * the source + * @return the local date + */ + public static LocalDate normaliseWeekDate(final LocalDate source) + { + LocalDate result; + + if (source == null) + { + result = source; + } + else + { + result = source.minusDays(source.getDayOfWeek().getValue() - 1); + } + + // + return result; + } + + /** + * Normalized week count list. + * + * @param source + * the source + * @param start + * the start + * @param end + * the end + * @return the date count list + */ + public static DateCountList normalizedWeekCountList(final DateCountList source, final LocalDate start, final LocalDate end) + { + DateCountList result; + + result = new DateCountList(); + + LocalDate normalizedEnd = normaliseWeekDate(end); + LocalDate date = normaliseWeekDate(start); + int index = source.indexOf(start.format(DateTimeFormatter.ofPattern("yyyy-M-w"))); + if (index == -1) + { + index = 0; + } + while (date.isBefore(normalizedEnd) || date.isEqual(normalizedEnd)) + { + String dateToken = date.format(DateTimeFormatter.ofPattern("yyyy-M-w")); + long count; + if (index < source.size()) + { + DateCount current = source.get(index); + AgirStatoolUtils.logger.info("===> " + dateToken + " " + current.getDate()); + if (StringUtils.equals(current.getDate(), dateToken)) + { + count = current.getCount(); + index += 1; + } + else + { + count = 0; + } + } + else + { + count = 0; + } + result.add(new DateCount(dateToken, count)); + date = date.plusWeeks(1); + } + + // + return result; + } + + /** + * Builds the week created count list. + * + * @param source + * the source + * @param start + * the start + * @return the date count list + */ + public static DateCountList normalizedWeekCountList(final DateCountMap source, final LocalDate start) + { + DateCountList result; + + result = new DateCountList(); + + LocalDate date = normaliseWeekDate(start); + LocalDate end = normaliseWeekDate(LocalDate.now()); + long count = 0; + while (date.isBefore(end) || date.isEqual(end)) + { + String dateToken = date.format(DateTimeFormatter.ofPattern("yyyy-M-w")); + DateCount current = source.get(dateToken); + if (current != null) + { + AgirStatoolUtils.logger.info("xxx> " + dateToken + " " + current.getDate()); + count += current.getCount(); + } + else + { + AgirStatoolUtils.logger.info("xxx> " + dateToken + " " + null); + } + result.add(new DateCount(dateToken, count)); + date = date.plusWeeks(1); + } + + // + return result; + } + /** * Gets the current time in long format. * diff --git a/src/org/april/agirstatool/core/IssueStats.java b/src/org/april/agirstatool/core/IssueStats.java index 373da6d..69b5435 100644 --- a/src/org/april/agirstatool/core/IssueStats.java +++ b/src/org/april/agirstatool/core/IssueStats.java @@ -51,7 +51,7 @@ public class IssueStats private LocalDateTime lastUpdate; private DateCountList weekCreatedIssueCounts; - private DateCountList weekClosedIssueCounts; + private DateCountList weekConcludedIssueCounts; /** * Instantiates a new issue stats. @@ -230,9 +230,9 @@ public class IssueStats return this.waitingCount; } - public DateCountList getWeekClosedIssueCounts() + public DateCountList getWeekConcludedIssueCounts() { - return this.weekClosedIssueCounts; + return this.weekConcludedIssueCounts; } public DateCountList getWeekCreatedIssueCounts() @@ -340,9 +340,9 @@ public class IssueStats this.waitingCount = waitingCount; } - public void setWeekClosedIssueCounts(final DateCountList weekClosedIssueCounts) + public void setWeekConcludedIssueCounts(final DateCountList weekConcludedIssueCounts) { - this.weekClosedIssueCounts = weekClosedIssueCounts; + this.weekConcludedIssueCounts = weekConcludedIssueCounts; } public void setWeekCreatedIssueCounts(final DateCountList weekCreatedIssueCounts) diff --git a/src/org/april/agirstatool/core/Project.java b/src/org/april/agirstatool/core/Project.java index 5db3488..1e3d22d 100644 --- a/src/org/april/agirstatool/core/Project.java +++ b/src/org/april/agirstatool/core/Project.java @@ -25,6 +25,13 @@ import java.time.LocalDateTime; */ public class Project { + public enum Type + { + ALONE, + CONSOLIDATED, + ROOT + } + private long id; private String identifier; private String name; @@ -85,6 +92,32 @@ public class Project return this.path; } + /** + * Gets the type. + * + * @return the type + */ + public Type getType() + { + Type result; + + if (this.id == 0) + { + result = Type.ROOT; + } + else if (this.name.startsWith("@")) + { + result = Type.ALONE; + } + else + { + result = Type.CONSOLIDATED; + } + + // + return result; + } + /** * Checks for child. * diff --git a/src/org/april/agirstatool/core/ProjectMode.java b/src/org/april/agirstatool/core/ProjectMode.java deleted file mode 100644 index 4d712c1..0000000 --- a/src/org/april/agirstatool/core/ProjectMode.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2020 Christian Pierre MOMON - * - * This file is part of AgirStatool, simple key value database. - * - * AgirStatool is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * AgirStatool is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with AgirStatool. If not, see . - */ -package org.april.agirstatool.core; - -/** - * The Enum ProjectMode. - */ -public enum ProjectMode -{ - ALONE, - CONSOLIDATED -} \ No newline at end of file diff --git a/src/org/april/agirstatool/core/pages/CreatedConcludedCountChartView.java b/src/org/april/agirstatool/core/pages/CreatedConcludedCountChartView.java index 33059e3..e891043 100644 --- a/src/org/april/agirstatool/core/pages/CreatedConcludedCountChartView.java +++ b/src/org/april/agirstatool/core/pages/CreatedConcludedCountChartView.java @@ -20,10 +20,10 @@ package org.april.agirstatool.core.pages; import java.io.IOException; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import org.apache.commons.codec.digest.DigestUtils; +import org.april.agirstatool.charts.DateCountList; import org.april.agirstatool.core.AgirStatool; import org.april.agirstatool.core.AgirStatoolException; import org.april.agirstatool.core.AgirStatoolUtils; @@ -56,22 +56,22 @@ public class CreatedConcludedCountChartView try { - logger.info("Building chartBar view…"); + logger.info("Building created/concluded chart view…"); - if (project.hasIssue() && !project.getName().equals("*")) + if (project.hasIssue()) { String source = XidynUtils.load(AgirStatool.class.getResource("/org/april/agirstatool/core/pages/chartLineView.xhtml")); String code = XidynUtils.extractBodyContent(source); code = code.replaceAll("myChart", "myChart_" + DigestUtils.md5Hex(title + "lineBar")); - StringList labels = buildWeekLabels(project.issueStats().getFirstCreate()); + StringList labels = buildWeekLabels(project.issueStats().getFirstCreate().toLocalDate()); code = code.replaceAll("labels: \\[.*\\]", "labels: " + AgirStatoolUtils.toJSonStrings(labels)); StringList values = project.issueStats().getWeekCreatedIssueCounts().toValueList(); code = code.replaceAll("data: \\[.*\\]", "data: " + AgirStatoolUtils.toJSonNumbers(values)); - values = project.issueStats().getWeekClosedIssueCounts().toValueList(); + values = project.issueStats().getWeekConcludedIssueCounts().toValueList(); code = code.replaceAll("data: \\[.*\\] ", "data: " + AgirStatoolUtils.toJSonNumbers(values)); result = code.toString(); @@ -101,28 +101,33 @@ public class CreatedConcludedCountChartView * @throws AgirStatoolException * the agir statool exception */ - public static String build3months(final String title, final Project project) throws AgirStatoolException + public static String build(final String title, final Project project, final int monthCount) throws AgirStatoolException { String result; try { - logger.info("Building created/closed 3 months chart view…"); + logger.info("Building created/closed x months chart view…"); if (project.hasIssue() && !project.getName().equals("*")) { String source = XidynUtils.load(AgirStatool.class.getResource("/org/april/agirstatool/core/pages/chartLineView.xhtml")); String code = XidynUtils.extractBodyContent(source); - code = code.replaceAll("myChart", "myChart_" + DigestUtils.md5Hex(title + "lineChart3months")); + code = code.replaceAll("myChart", "myChart_" + DigestUtils.md5Hex(title + "lineChartXMonths")); - StringList labels = buildWeekLabels(project.issueStats().getFirstCreate()); + LocalDate start = LocalDate.now().minusMonths(monthCount); + LocalDate end = LocalDate.now(); + + StringList labels = buildWeekLabels(start); code = code.replaceAll("labels: \\[.*\\]", "labels: " + AgirStatoolUtils.toJSonStrings(labels)); - StringList values = project.issueStats().getWeekCreatedIssueCounts().toValueList(); + DateCountList dates = project.issueStats().getWeekCreatedIssueCounts(); + StringList values = AgirStatoolUtils.normalizedWeekCountList(dates, start, end).toValueList(); code = code.replaceAll("data: \\[.*\\]", "data: " + AgirStatoolUtils.toJSonNumbers(values)); - values = project.issueStats().getWeekClosedIssueCounts().toValueList(); + dates = project.issueStats().getWeekConcludedIssueCounts(); + values = AgirStatoolUtils.normalizedWeekCountList(dates, start, end).toValueList(); code = code.replaceAll("data: \\[.*\\] ", "data: " + AgirStatoolUtils.toJSonNumbers(values)); result = code.toString(); @@ -141,7 +146,14 @@ public class CreatedConcludedCountChartView return result; } - private static StringList buildWeekLabels(final LocalDateTime start) + /** + * Builds the week labels. + * + * @param start + * the start + * @return the string list + */ + private static StringList buildWeekLabels(final LocalDate start) { StringList result; @@ -149,21 +161,12 @@ public class CreatedConcludedCountChartView if (start != null) { - LocalDate end = LocalDate.now(); - LocalDate date = start.toLocalDate(); + LocalDate end = AgirStatoolUtils.normaliseWeekDate(LocalDate.now()); + LocalDate date = AgirStatoolUtils.normaliseWeekDate(start); while (date.isBefore(end) || date.isEqual(end)) { - // if - // (date.get(WeekFields.of(Locale.getDefault()).weekOfMonth()) - // == 1) - // { String label = date.format(DateTimeFormatter.ofPattern("yyyy-MMM")); result.add(label); - // } - // else - // { - // result.add(""); - // } date = date.plusWeeks(1); } } diff --git a/src/org/april/agirstatool/core/pages/ProjectPage.java b/src/org/april/agirstatool/core/pages/ProjectPage.java index 83261d1..4b3152e 100644 --- a/src/org/april/agirstatool/core/pages/ProjectPage.java +++ b/src/org/april/agirstatool/core/pages/ProjectPage.java @@ -59,6 +59,8 @@ public class ProjectPage data.setAttribute("agirLink", "href", "https://agir.april.org/projects/" + project.getIdentifier() + "/issues"); data.setContent("issueCreatedClosedChart", CreatedConcludedCountChartView.build("Created/closed Count", project)); + data.setContent("issueCreatedClosed3MonthsChart", CreatedConcludedCountChartView.build("Created/closed 3 months Count", project, 3)); + data.setContent("issueCreatedClosed6MonthsChart", CreatedConcludedCountChartView.build("Created/closed 6 months Count", project, 6)); data.setContent("issueRawChart", IssueStatChartView.build("Issue Raw Count", project)); data.setContent("issueGroupedChart", IssueStatChartView.buildGrouped("Issue Grouped Count", project)); diff --git a/src/org/april/agirstatool/core/pages/project.xhtml b/src/org/april/agirstatool/core/pages/project.xhtml index 782fd02..ff6edea 100644 --- a/src/org/april/agirstatool/core/pages/project.xhtml +++ b/src/org/april/agirstatool/core/pages/project.xhtml @@ -13,6 +13,8 @@

Agir Statool – Project n/a

+
ISSUES CREATED/CLOSED 6 MONTHS CHART
+
ISSUES CREATED/CLOSED 3 MONTHS CHART
ISSUES CREATED/CLOSED CHART
Grouped