Added Created/Concluded issue chart.
This commit is contained in:
parent
e918ae0884
commit
c3daa7973b
11 changed files with 655 additions and 42 deletions
72
src/org/april/agirstatool/charts/DateCount.java
Normal file
72
src/org/april/agirstatool/charts/DateCount.java
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Christian Pierre MOMON <christian.momon@devinsy.fr>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.april.agirstatool.charts;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class Projects.
|
||||||
|
*/
|
||||||
|
public class DateCount
|
||||||
|
{
|
||||||
|
private String date;
|
||||||
|
private long count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new date count.
|
||||||
|
*
|
||||||
|
* @param date
|
||||||
|
* the date
|
||||||
|
* @param count
|
||||||
|
* the count
|
||||||
|
*/
|
||||||
|
public DateCount(final String date, final long count)
|
||||||
|
{
|
||||||
|
setDate(date);
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCount()
|
||||||
|
{
|
||||||
|
return this.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDate()
|
||||||
|
{
|
||||||
|
return this.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCount(final long count)
|
||||||
|
{
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDate(final String date)
|
||||||
|
{
|
||||||
|
if (StringUtils.isBlank(date))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Null parameter.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
59
src/org/april/agirstatool/charts/DateCountList.java
Normal file
59
src/org/april/agirstatool/charts/DateCountList.java
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Christian Pierre MOMON <christian.momon@devinsy.fr>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.april.agirstatool.charts;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import fr.devinsy.strings.StringList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class Projects.
|
||||||
|
*/
|
||||||
|
public class DateCountList extends ArrayList<DateCount>
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = -5526492552751712533L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new date count map.
|
||||||
|
*/
|
||||||
|
public DateCountList()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To value list.
|
||||||
|
*
|
||||||
|
* @return the string list
|
||||||
|
*/
|
||||||
|
public StringList toValueList()
|
||||||
|
{
|
||||||
|
StringList result;
|
||||||
|
|
||||||
|
result = new StringList();
|
||||||
|
|
||||||
|
for (DateCount item : this)
|
||||||
|
{
|
||||||
|
result.append(item.getCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
37
src/org/april/agirstatool/charts/DateCountMap.java
Normal file
37
src/org/april/agirstatool/charts/DateCountMap.java
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Christian Pierre MOMON <christian.momon@devinsy.fr>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.april.agirstatool.charts;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class Projects.
|
||||||
|
*/
|
||||||
|
public class DateCountMap extends HashMap<String, DateCount>
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = -5526492552751712533L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new date count map.
|
||||||
|
*/
|
||||||
|
public DateCountMap()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,8 +25,13 @@ import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.april.agirstatool.charts.DateCount;
|
||||||
|
import org.april.agirstatool.charts.DateCountList;
|
||||||
|
import org.april.agirstatool.charts.DateCountMap;
|
||||||
import org.april.agirstatool.cli.SQLUtils;
|
import org.april.agirstatool.cli.SQLUtils;
|
||||||
import org.april.agirstatool.core.pages.ProjectPage;
|
import org.april.agirstatool.core.pages.ProjectPage;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -194,7 +199,8 @@ public class AgirStatool
|
||||||
Project root = listProjectsAsTree();
|
Project root = listProjectsAsTree();
|
||||||
|
|
||||||
// Create welcome page.
|
// Create welcome page.
|
||||||
refreshWelcomePage(root);
|
refreshPage(root);
|
||||||
|
FileUtils.copyFile(new File(this.targetDirectory, "all.xhtml"), new File(this.targetDirectory, "index.xhtml"));
|
||||||
|
|
||||||
// Create one page per project.
|
// Create one page per project.
|
||||||
for (Project project : root.subProjects())
|
for (Project project : root.subProjects())
|
||||||
|
@ -213,6 +219,154 @@ public class AgirStatool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch week concluded count.
|
||||||
|
*
|
||||||
|
* In Redmine database: closed_on when resolved, rejected or closed.
|
||||||
|
*
|
||||||
|
* @param project
|
||||||
|
* the project
|
||||||
|
* @param consolidated
|
||||||
|
* the consolidated
|
||||||
|
* @return the date count map
|
||||||
|
* @throws AgirStatoolException
|
||||||
|
* the agir statool exception
|
||||||
|
*/
|
||||||
|
public DateCountMap fetchWeekConcludedCount(final Project project, final ProjectMode mode) throws AgirStatoolException
|
||||||
|
{
|
||||||
|
DateCountMap result;
|
||||||
|
|
||||||
|
result = new DateCountMap();
|
||||||
|
|
||||||
|
//
|
||||||
|
PreparedStatement statement = null;
|
||||||
|
ResultSet resultSet = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StringList subSql = new StringList();
|
||||||
|
if (mode == ProjectMode.CONSOLIDATED)
|
||||||
|
{
|
||||||
|
subSql.append("select ");
|
||||||
|
subSql.append(" id ");
|
||||||
|
subSql.append("from ");
|
||||||
|
subSql.append(" projects as childProject ");
|
||||||
|
subSql.append("where ");
|
||||||
|
subSql.append(" (childProject.id=" + project.getId() + " or childProject.parent_id=" + project.getId() + ")");
|
||||||
|
subSql.append(" and childProject.status=1 and childProject.is_public=1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
subSql.append(project.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList sql = new StringList();
|
||||||
|
sql.append("SELECT");
|
||||||
|
sql.append(" concat_ws('-', year(closed_on), weekofyear(closed_on)) as date, ");
|
||||||
|
sql.append(" count(*) as count ");
|
||||||
|
sql.append("FROM ");
|
||||||
|
sql.append(" issues ");
|
||||||
|
sql.append("WHERE ");
|
||||||
|
sql.append(" project_id in (" + subSql.toString() + ") and closed_on is not null ");
|
||||||
|
sql.append("GROUP BY ");
|
||||||
|
sql.append(" date;");
|
||||||
|
|
||||||
|
// System.out.println(sql.toStringSeparatedBy("\n"));
|
||||||
|
|
||||||
|
this.connection.setAutoCommit(true);
|
||||||
|
statement = this.connection.prepareStatement(sql.toString());
|
||||||
|
resultSet = statement.executeQuery();
|
||||||
|
|
||||||
|
while (resultSet.next())
|
||||||
|
{
|
||||||
|
result.put(resultSet.getString(1), new DateCount(resultSet.getString(1), resultSet.getLong(2)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SQLException exception)
|
||||||
|
{
|
||||||
|
throw new AgirStatoolException("Error fetching day closed count: " + exception.getMessage(), exception);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
SQLUtils.closeQuietly(statement, resultSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch week created count.
|
||||||
|
*
|
||||||
|
* @param project
|
||||||
|
* the project
|
||||||
|
* @param consolidated
|
||||||
|
* the consolidated
|
||||||
|
* @return the date count map
|
||||||
|
* @throws AgirStatoolException
|
||||||
|
* the agir statool exception
|
||||||
|
*/
|
||||||
|
public DateCountMap fetchWeekCreatedCount(final Project project, final ProjectMode mode) throws AgirStatoolException
|
||||||
|
{
|
||||||
|
DateCountMap result;
|
||||||
|
|
||||||
|
result = new DateCountMap();
|
||||||
|
|
||||||
|
//
|
||||||
|
PreparedStatement statement = null;
|
||||||
|
ResultSet resultSet = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StringList subSql = new StringList();
|
||||||
|
if (mode == ProjectMode.CONSOLIDATED)
|
||||||
|
{
|
||||||
|
subSql.append("select ");
|
||||||
|
subSql.append(" id ");
|
||||||
|
subSql.append("from ");
|
||||||
|
subSql.append(" projects as childProject ");
|
||||||
|
subSql.append("where ");
|
||||||
|
subSql.append(" (childProject.id=" + project.getId() + " or childProject.parent_id=" + project.getId() + ")");
|
||||||
|
subSql.append(" and childProject.status=1 and childProject.is_public=1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
subSql.append(project.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList sql = new StringList();
|
||||||
|
sql.append("SELECT ");
|
||||||
|
sql.append(" concat_ws('-', year(created_on), weekofyear(created_on)) as date, ");
|
||||||
|
sql.append(" count(*) as count ");
|
||||||
|
sql.append("FROM ");
|
||||||
|
sql.append(" issues ");
|
||||||
|
sql.append("WHERE ");
|
||||||
|
sql.append(" project_id in (" + subSql.toString() + ") ");
|
||||||
|
sql.append("GROUP BY ");
|
||||||
|
sql.append(" date;");
|
||||||
|
|
||||||
|
// logger.debug(sql.toStringSeparatedBy("\n"));
|
||||||
|
|
||||||
|
this.connection.setAutoCommit(true);
|
||||||
|
statement = this.connection.prepareStatement(sql.toString());
|
||||||
|
resultSet = statement.executeQuery();
|
||||||
|
|
||||||
|
while (resultSet.next())
|
||||||
|
{
|
||||||
|
result.put(resultSet.getString(1), new DateCount(resultSet.getString(1), resultSet.getLong(2)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SQLException exception)
|
||||||
|
{
|
||||||
|
throw new AgirStatoolException("Error fetching day created count: " + exception.getMessage(), exception);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
SQLUtils.closeQuietly(statement, resultSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the project all.
|
* Gets the project all.
|
||||||
*
|
*
|
||||||
|
@ -240,10 +394,10 @@ public class AgirStatool
|
||||||
//
|
//
|
||||||
StringList sql = new StringList();
|
StringList sql = new StringList();
|
||||||
sql.append("SELECT");
|
sql.append("SELECT");
|
||||||
sql.append(" 0,");
|
sql.append(" 0 as root_project_id,");
|
||||||
sql.append(" 'all',");
|
sql.append(" 'all' as root_project_identifier,");
|
||||||
sql.append(" '*',");
|
sql.append(" '*' as root_project_name,");
|
||||||
sql.append(" null,");
|
sql.append(" null as root_parent_id,");
|
||||||
sql.append(" (select count(*) from projects where status = 1 and is_public = 1) as child_count, ");
|
sql.append(" (select count(*) from projects where status = 1 and is_public = 1) as child_count, ");
|
||||||
sql.append(" (select max(updated_on) from projects where status = 1 and is_public = 1) as last_update,");
|
sql.append(" (select max(updated_on) from projects where status = 1 and is_public = 1) as last_update,");
|
||||||
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ")) as issue_count,");
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ")) as issue_count,");
|
||||||
|
@ -402,6 +556,36 @@ public class AgirStatool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill created and concluded issues history.
|
||||||
|
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());
|
||||||
|
project.issueStats().setWeekCreatedIssueCounts(counts);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
DateCountMap map = fetchWeekConcludedCount(project, mode);
|
||||||
|
DateCountList counts = normalizedWeekCountList(map, project.issueStats().getFirstCreate().toLocalDate());
|
||||||
|
project.issueStats().setWeekClosedIssueCounts(counts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info("Fetching Created/Closed history done.");
|
||||||
|
|
||||||
// Transform as tree.
|
// Transform as tree.
|
||||||
for (Project project : projects)
|
for (Project project : projects)
|
||||||
{
|
{
|
||||||
|
@ -432,7 +616,7 @@ public class AgirStatool
|
||||||
{
|
{
|
||||||
Projects result;
|
Projects result;
|
||||||
|
|
||||||
result = listProjectsWithStats(false);
|
result = listProjectsWithStats(ProjectMode.ALONE);
|
||||||
//
|
//
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -446,7 +630,7 @@ public class AgirStatool
|
||||||
* @throws AgirStatoolException
|
* @throws AgirStatoolException
|
||||||
* the agir statool exception
|
* the agir statool exception
|
||||||
*/
|
*/
|
||||||
public Projects listProjectsWithStats(final boolean consolidated) throws AgirStatoolException
|
public Projects listProjectsWithStats(final ProjectMode mode) throws AgirStatoolException
|
||||||
{
|
{
|
||||||
Projects result;
|
Projects result;
|
||||||
|
|
||||||
|
@ -458,9 +642,8 @@ public class AgirStatool
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
StringList subSql = new StringList();
|
StringList subSql = new StringList();
|
||||||
if (consolidated)
|
if (mode == ProjectMode.CONSOLIDATED)
|
||||||
{
|
{
|
||||||
|
|
||||||
subSql.append("select ");
|
subSql.append("select ");
|
||||||
subSql.append(" id ");
|
subSql.append(" id ");
|
||||||
subSql.append("from ");
|
subSql.append("from ");
|
||||||
|
@ -585,7 +768,41 @@ public class AgirStatool
|
||||||
{
|
{
|
||||||
Projects result;
|
Projects result;
|
||||||
|
|
||||||
result = listProjectsWithStats(true);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
return result;
|
return result;
|
||||||
|
@ -638,28 +855,4 @@ public class AgirStatool
|
||||||
throw new AgirStatoolException("Error refreshing page: " + exception.getMessage(), exception);
|
throw new AgirStatoolException("Error refreshing page: " + exception.getMessage(), exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh welcomme page.
|
|
||||||
*
|
|
||||||
* @param project
|
|
||||||
* the project
|
|
||||||
* @throws AgirStatoolException
|
|
||||||
* the agir statool exception
|
|
||||||
*/
|
|
||||||
public void refreshWelcomePage(final Project root) throws AgirStatoolException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (root != null)
|
|
||||||
{
|
|
||||||
String page = ProjectPage.build(root);
|
|
||||||
FileUtils.write(new File(this.targetDirectory, "index.xhtml"), page, StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException exception)
|
|
||||||
{
|
|
||||||
throw new AgirStatoolException("Error refreshing welcome page: " + exception.getMessage(), exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ package org.april.agirstatool.core;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import org.april.agirstatool.charts.DateCountList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class Projects.
|
* The Class Projects.
|
||||||
*/
|
*/
|
||||||
|
@ -48,6 +50,9 @@ public class IssueStats
|
||||||
private LocalDateTime firstCreate;
|
private LocalDateTime firstCreate;
|
||||||
private LocalDateTime lastUpdate;
|
private LocalDateTime lastUpdate;
|
||||||
|
|
||||||
|
private DateCountList weekCreatedIssueCounts;
|
||||||
|
private DateCountList weekClosedIssueCounts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new issue stats.
|
* Instantiates a new issue stats.
|
||||||
*/
|
*/
|
||||||
|
@ -225,6 +230,16 @@ public class IssueStats
|
||||||
return this.waitingCount;
|
return this.waitingCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DateCountList getWeekClosedIssueCounts()
|
||||||
|
{
|
||||||
|
return this.weekClosedIssueCounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateCountList getWeekCreatedIssueCounts()
|
||||||
|
{
|
||||||
|
return this.weekCreatedIssueCounts;
|
||||||
|
}
|
||||||
|
|
||||||
public void setClosedCount(final long closedCount)
|
public void setClosedCount(final long closedCount)
|
||||||
{
|
{
|
||||||
this.closedCount = closedCount;
|
this.closedCount = closedCount;
|
||||||
|
@ -324,4 +339,14 @@ public class IssueStats
|
||||||
{
|
{
|
||||||
this.waitingCount = waitingCount;
|
this.waitingCount = waitingCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setWeekClosedIssueCounts(final DateCountList weekClosedIssueCounts)
|
||||||
|
{
|
||||||
|
this.weekClosedIssueCounts = weekClosedIssueCounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWeekCreatedIssueCounts(final DateCountList weekCreatedIssueCounts)
|
||||||
|
{
|
||||||
|
this.weekCreatedIssueCounts = weekCreatedIssueCounts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,26 @@ public class Project
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean hasIssue()
|
||||||
|
{
|
||||||
|
boolean result;
|
||||||
|
|
||||||
|
if (this.issueStats().getCount() == 0)
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public IssueStats issueStats()
|
public IssueStats issueStats()
|
||||||
{
|
{
|
||||||
return this.stats;
|
return this.stats;
|
||||||
|
|
28
src/org/april/agirstatool/core/ProjectMode.java
Normal file
28
src/org/april/agirstatool/core/ProjectMode.java
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Christian Pierre MOMON <christian.momon@devinsy.fr>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.april.agirstatool.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Enum ProjectMode.
|
||||||
|
*/
|
||||||
|
public enum ProjectMode
|
||||||
|
{
|
||||||
|
ALONE,
|
||||||
|
CONSOLIDATED
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Christian Pierre MOMON <christian.momon@devinsy.fr>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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.core.AgirStatool;
|
||||||
|
import org.april.agirstatool.core.AgirStatoolException;
|
||||||
|
import org.april.agirstatool.core.AgirStatoolUtils;
|
||||||
|
import org.april.agirstatool.core.Project;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import fr.devinsy.strings.StringList;
|
||||||
|
import fr.devinsy.xidyn.utils.XidynUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class projectsRawPageBuilder.
|
||||||
|
*/
|
||||||
|
public class CreatedConcludedCountChartView
|
||||||
|
{
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(CreatedConcludedCountChartView.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the.
|
||||||
|
*
|
||||||
|
* @param projects
|
||||||
|
* the projects
|
||||||
|
* @return the string
|
||||||
|
* @throws AgirStatoolException
|
||||||
|
* the agir statool exception
|
||||||
|
*/
|
||||||
|
public static String build(final String title, final Project project) throws AgirStatoolException
|
||||||
|
{
|
||||||
|
String result;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
logger.info("Building chartBar 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 + "lineBar"));
|
||||||
|
|
||||||
|
StringList labels = buildWeekLabels(project.issueStats().getFirstCreate());
|
||||||
|
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();
|
||||||
|
code = code.replaceAll("data: \\[.*\\] ", "data: " + AgirStatoolUtils.toJSonNumbers(values));
|
||||||
|
|
||||||
|
result = code.toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = "No issue.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException exception)
|
||||||
|
{
|
||||||
|
throw new AgirStatoolException("Error building ProjectsRaw view: " + exception.getMessage(), exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the 3 months.
|
||||||
|
*
|
||||||
|
* @param title
|
||||||
|
* the title
|
||||||
|
* @param project
|
||||||
|
* the project
|
||||||
|
* @return the string
|
||||||
|
* @throws AgirStatoolException
|
||||||
|
* the agir statool exception
|
||||||
|
*/
|
||||||
|
public static String build3months(final String title, final Project project) throws AgirStatoolException
|
||||||
|
{
|
||||||
|
String result;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
logger.info("Building created/closed 3 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"));
|
||||||
|
|
||||||
|
StringList labels = buildWeekLabels(project.issueStats().getFirstCreate());
|
||||||
|
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();
|
||||||
|
code = code.replaceAll("data: \\[.*\\] ", "data: " + AgirStatoolUtils.toJSonNumbers(values));
|
||||||
|
|
||||||
|
result = code.toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = "No issue.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException exception)
|
||||||
|
{
|
||||||
|
throw new AgirStatoolException("Error building ProjectsRaw view: " + exception.getMessage(), exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StringList buildWeekLabels(final LocalDateTime start)
|
||||||
|
{
|
||||||
|
StringList result;
|
||||||
|
|
||||||
|
result = new StringList();
|
||||||
|
|
||||||
|
if (start != null)
|
||||||
|
{
|
||||||
|
LocalDate end = LocalDate.now();
|
||||||
|
LocalDate date = start.toLocalDate();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,9 +55,11 @@ public class ProjectPage
|
||||||
|
|
||||||
TagDataManager data = new TagDataManager();
|
TagDataManager data = new TagDataManager();
|
||||||
|
|
||||||
data.setContent("projectName", project.getName());
|
data.setContent("agirLink", project.getName());
|
||||||
data.setAttribute("agirLink", "href", "https://agir.april.org/projects/" + project.getIdentifier() + "/issues");
|
data.setAttribute("agirLink", "href", "https://agir.april.org/projects/" + project.getIdentifier() + "/issues");
|
||||||
|
|
||||||
|
data.setContent("issueCreatedClosedChart", CreatedConcludedCountChartView.build("Created/closed Count", project));
|
||||||
|
|
||||||
data.setContent("issueRawChart", IssueStatChartView.build("Issue Raw Count", project));
|
data.setContent("issueRawChart", IssueStatChartView.build("Issue Raw Count", project));
|
||||||
data.setContent("issueGroupedChart", IssueStatChartView.buildGrouped("Issue Grouped Count", project));
|
data.setContent("issueGroupedChart", IssueStatChartView.buildGrouped("Issue Grouped Count", project));
|
||||||
data.setContent("unassignedRawChart", UnassignedPolarChartView.build("Unassigned Raw Count", project));
|
data.setContent("unassignedRawChart", UnassignedPolarChartView.build("Unassigned Raw Count", project));
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<script src="Chart.bundle.min.js"></script>
|
<script src="Chart.bundle.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div style="width: 800px; height: 400px; text-align: center; margin: 0 0; border: 1px solid red;">
|
<div style="width: 100%; height: 400px; text-align: center; margin: 0 0; border: 1px solid red;">
|
||||||
<canvas id="myChart" width="100%" height="100%"></canvas>
|
<canvas id="myChart" width="100%" height="100%"></canvas>
|
||||||
<script>
|
<script>
|
||||||
var ctx = document.getElementById('myChart');
|
var ctx = document.getElementById('myChart');
|
||||||
|
@ -20,7 +20,7 @@ var myChart = new Chart(ctx,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data:
|
data:
|
||||||
{
|
{
|
||||||
labels: ['S01', 'S02', 'S03', 'S04', 'S05', 'S06'],
|
labels: ['S01', 'S02', 'S03', 'S04', 'S05'],
|
||||||
datasets:
|
datasets:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -31,16 +31,18 @@ var myChart = new Chart(ctx,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
fill: true,
|
fill: true,
|
||||||
/* cubicInterpolationMode: 'monotone', */
|
/* cubicInterpolationMode: 'monotone', */
|
||||||
lineTension: 0
|
lineTension: 0,
|
||||||
|
pointBorderWidth: 0.00000001
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Concluded',
|
label: 'Closed',
|
||||||
data: [1, 5, 9, 13, 15, 22],
|
data: [1, 5, 9, 13, 15, 22] ,
|
||||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||||
borderColor: 'rgba(75, 192, 192, 1)',
|
borderColor: 'rgba(75, 192, 192, 1)',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
fill: true,
|
fill: true,
|
||||||
lineTension: 0
|
lineTension: 0,
|
||||||
|
pointBorderWidth: 0.00000001
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div style="margin: 10px;">
|
<div style="margin: 10px;">
|
||||||
<h1><a href="index.xhtml">Agir Statool</a> – <a id="agirLink" href="#">Project <span id="projectName">n/a</span></a></h1>
|
<h1><a href="index.xhtml">Agir Statool</a> – Project <a id="agirLink" href="#">n/a</a></h1>
|
||||||
|
<div id="issueCreatedClosedChart">ISSUES CREATED/CLOSED CHART</div>
|
||||||
<div style="margin: 4px;">
|
<div style="margin: 4px;">
|
||||||
<span style="display: inline-block; padding-top: 6px; vertical-align: text-top;">Grouped</span>
|
<span style="display: inline-block; padding-top: 6px; vertical-align: text-top;">Grouped</span>
|
||||||
<label class="switch">
|
<label class="switch">
|
||||||
|
|
Loading…
Reference in a new issue