994 lines
31 KiB
Java
994 lines
31 KiB
Java
/*
|
|
* 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;
|
|
|
|
import java.io.File;
|
|
import java.io.FileFilter;
|
|
import java.io.IOException;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.sql.Connection;
|
|
import java.sql.PreparedStatement;
|
|
import java.sql.ResultSet;
|
|
import java.sql.SQLException;
|
|
import java.time.LocalDateTime;
|
|
import java.time.ZoneOffset;
|
|
|
|
import org.apache.commons.io.FileUtils;
|
|
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
|
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.core.pages.ProjectPage;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import fr.devinsy.strings.StringList;
|
|
|
|
/**
|
|
* The class AgirStatool.
|
|
*
|
|
* @author Christian Pierre MOMON
|
|
*/
|
|
public class AgirStatool
|
|
{
|
|
private static Logger logger = LoggerFactory.getLogger(AgirStatool.class);
|
|
|
|
public static final char ALONE_INDICATOR = '@';
|
|
|
|
private Connection connection;
|
|
private File targetDirectory;
|
|
|
|
/**
|
|
* Instantiates a new agir statool.
|
|
*
|
|
* @param connector
|
|
* the connector
|
|
* @param targetDirectory
|
|
* the target directory
|
|
*/
|
|
public AgirStatool(final Connection connector, final File targetDirectory)
|
|
{
|
|
if (connector == null)
|
|
{
|
|
throw new IllegalArgumentException("Null parameter (connector).");
|
|
}
|
|
else if (targetDirectory == null)
|
|
{
|
|
throw new IllegalArgumentException("Null parameter (target directory).");
|
|
}
|
|
else if (!targetDirectory.exists())
|
|
{
|
|
throw new IllegalArgumentException("Target directory does not exist.");
|
|
}
|
|
else if (!targetDirectory.isDirectory())
|
|
{
|
|
throw new IllegalArgumentException("Target directory is not a directory.");
|
|
}
|
|
else
|
|
{
|
|
this.connection = connector;
|
|
this.targetDirectory = targetDirectory;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds the text project extended list.
|
|
*
|
|
* @return the string list
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public StringList doBuildTextProjectExtendedList() throws AgirStatoolException
|
|
{
|
|
StringList result;
|
|
|
|
result = new StringList();
|
|
|
|
Projects projects = fetchProjectsWithoutSubStats().sortByName();
|
|
String header = String.format("%3s %-30s %-30s %s %s %6s %6s %7s %7s %7s %7s %9s %7s %7s",
|
|
"ID",
|
|
"Identifier",
|
|
"Name",
|
|
"ParentId",
|
|
"Child",
|
|
"Count",
|
|
"New",
|
|
"Ongoing",
|
|
"Resolved",
|
|
"Closed",
|
|
"Rejected",
|
|
"Confirmed",
|
|
"Maybe",
|
|
"Waiting");
|
|
result.appendln(header);
|
|
for (Project project : projects)
|
|
{
|
|
String line = String.format("%3d %-30s %-30s %4d %4d %7d %7d %7d %7d %7d %7d %9d %7d %7s",
|
|
project.getId(),
|
|
project.getIdentifier(),
|
|
project.getName(),
|
|
project.getParentId(),
|
|
project.getChildCount(),
|
|
project.issueStats().getCount(),
|
|
project.issueStats().getNewCount(),
|
|
project.issueStats().getOngoingCount(),
|
|
project.issueStats().getResolvedCount(),
|
|
project.issueStats().getClosedCount(),
|
|
project.issueStats().getRejectedCount(),
|
|
project.issueStats().getConfirmedCount(),
|
|
project.issueStats().getMaybeCount(),
|
|
project.issueStats().getWaitingCount());
|
|
result.appendln(line);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Do build text project list.
|
|
*
|
|
* @return the string list
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public StringList doBuildTextProjectList() throws AgirStatoolException
|
|
{
|
|
StringList result;
|
|
|
|
result = new StringList();
|
|
|
|
Projects projects = listProjects().sortByName();
|
|
String header = String.format("%3s %-30s %-30s %s %s", "ID", "Identifier", "Name", "ParentId", "ChildCount");
|
|
result.appendln(header);
|
|
for (Project project : projects)
|
|
{
|
|
String line = String.format("%3d %-30s %-30s %4d %4d",
|
|
project.getId(),
|
|
project.getIdentifier(),
|
|
project.getName(),
|
|
project.getParentId(),
|
|
project.getChildCount());
|
|
result.appendln(line);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Do clear all pages.
|
|
*
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public void doClearAllPages() throws AgirStatoolException
|
|
{
|
|
try
|
|
{
|
|
FileFilter filter = new WildcardFileFilter(new String[] { "*.xhtml", "*.html", "*.css", "*.js" });
|
|
for (File file : this.targetDirectory.listFiles(filter))
|
|
{
|
|
FileUtils.forceDelete(file);
|
|
}
|
|
}
|
|
catch (IOException exception)
|
|
{
|
|
throw new AgirStatoolException("Error clearing target directory: " + exception.getMessage(), exception);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Do refresh pages.
|
|
*
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public void doUpdatePages() throws AgirStatoolException
|
|
{
|
|
try
|
|
{
|
|
// Copy CSS file.
|
|
if (!new File(this.targetDirectory, "index.html").exists())
|
|
{
|
|
FileUtils.copyURLToFile(AgirStatool.class.getResource("/org/april/agirstatool/core/pages/index.html"), new File(this.targetDirectory, "index.html"));
|
|
FileUtils.copyURLToFile(AgirStatool.class.getResource("/org/april/agirstatool/core/pages/agirstatool.css"), new File(this.targetDirectory, "agirstatool.css"));
|
|
FileUtils.copyURLToFile(AgirStatool.class.getResource("/org/april/agirstatool/core/pages/Chart.bundle.min.js"), new File(this.targetDirectory, "Chart.bundle.min.js"));
|
|
}
|
|
|
|
//
|
|
Project root = listProjectsAsTree();
|
|
|
|
// Create welcome page.
|
|
updatePage(root);
|
|
FileUtils.copyFile(new File(this.targetDirectory, "all.xhtml"), new File(this.targetDirectory, "index.xhtml"));
|
|
|
|
// Create one page per project.
|
|
for (Project project : root.subProjects())
|
|
{
|
|
updatePage(project);
|
|
for (Project subProject : project.subProjects())
|
|
{
|
|
updatePage(subProject);
|
|
}
|
|
}
|
|
}
|
|
catch (IOException exception)
|
|
{
|
|
throw new AgirStatoolException("Error refreshing all: " + exception.getMessage(), exception);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch issue open closed dates.
|
|
*
|
|
* @param project
|
|
* the project
|
|
* @return the date count map
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public Issues fetchIssue(final Project project) throws AgirStatoolException
|
|
{
|
|
Issues result;
|
|
|
|
result = new Issues();
|
|
|
|
//
|
|
PreparedStatement statement = null;
|
|
ResultSet resultSet = null;
|
|
try
|
|
{
|
|
StringList subSql = new StringList();
|
|
if (project.getType() == Project.Type.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 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());
|
|
}
|
|
|
|
StringList sql = new StringList();
|
|
sql.append("SELECT");
|
|
sql.append(" id, ");
|
|
sql.append(" project_id, ");
|
|
sql.append(" created_on, ");
|
|
sql.append(" closed_on ");
|
|
sql.append("FROM ");
|
|
sql.append(" issues ");
|
|
sql.append("WHERE ");
|
|
sql.append(" project_id in (" + subSql.toString() + ") ");
|
|
|
|
// System.out.println(sql.toStringSeparatedBy("\n"));
|
|
|
|
this.connection.setAutoCommit(true);
|
|
statement = this.connection.prepareStatement(sql.toString());
|
|
resultSet = statement.executeQuery();
|
|
|
|
while (resultSet.next())
|
|
{
|
|
long id = resultSet.getInt(1);
|
|
long projectId = resultSet.getInt(2);
|
|
LocalDateTime createdOn = SQLUtils.toLocalDateTime(resultSet.getTimestamp(3));
|
|
LocalDateTime closedOn = SQLUtils.toLocalDateTime(resultSet.getTimestamp(4));
|
|
|
|
Issue issue = new Issue(id, projectId, createdOn, closedOn);
|
|
|
|
result.add(issue);
|
|
}
|
|
}
|
|
catch (SQLException exception)
|
|
{
|
|
throw new AgirStatoolException("Error fetching day closed count: " + exception.getMessage(), exception);
|
|
}
|
|
finally
|
|
{
|
|
SQLUtils.closeQuietly(statement, resultSet);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* List projects extended.
|
|
*
|
|
* @return the projects
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public Projects fetchProjectsWithoutSubStats() throws AgirStatoolException
|
|
{
|
|
Projects result;
|
|
|
|
result = fetchProjectsWithStats(Project.Type.ALONE);
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* List projects with stats.
|
|
*
|
|
* @param type
|
|
* the type
|
|
* @return the projects
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public Projects fetchProjectsWithStats(final Project.Type type) throws AgirStatoolException
|
|
{
|
|
Projects result;
|
|
|
|
result = new Projects();
|
|
|
|
//
|
|
PreparedStatement statement = null;
|
|
ResultSet resultSet = null;
|
|
try
|
|
{
|
|
StringList subSql = new StringList();
|
|
if (type == Project.Type.CONSOLIDATED)
|
|
{
|
|
subSql.append("select ");
|
|
subSql.append(" id ");
|
|
subSql.append("from ");
|
|
subSql.append(" projects as childProject ");
|
|
subSql.append("where ");
|
|
subSql.append(" (childProject.id=currentProject.id or childProject.parent_id=currentProject.id)");
|
|
subSql.append(" and childProject.status=1 and childProject.is_public=1");
|
|
}
|
|
else
|
|
{
|
|
subSql.append("currentProject.id");
|
|
}
|
|
|
|
StringList sql = new StringList();
|
|
sql.append("SELECT");
|
|
sql.append(" id,");
|
|
sql.append(" identifier,");
|
|
sql.append(" name,");
|
|
sql.append(" parent_id,");
|
|
sql.append(" (select count(*) from projects as subproject where subproject.parent_id=currentProject.id and subproject.status = 1 and subproject.is_public = 1) as child_count, ");
|
|
sql.append(" updated_on,");
|
|
|
|
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() + ") and issues.status_id= 1) as new_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 2) as ongoing_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 3) as resolved_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 5) as closed_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 6) as rejected_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 7) as confirmed_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id=15) as maybe_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id=16) as waiting_issue_count, ");
|
|
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.assigned_to_id is null) as unassigned_issue_count,");
|
|
sql.append(
|
|
" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 1 and issues.assigned_to_id is null) as unassigned_new_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString()
|
|
+ ") and issues.status_id= 2 and issues.assigned_to_id is null) as unassigned_ongoing_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString()
|
|
+ ") and issues.status_id= 3 and issues.assigned_to_id is null) as unassigned_resolved_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString()
|
|
+ ") and issues.status_id= 5 and issues.assigned_to_id is null) as unassigned_closed_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString()
|
|
+ ") and issues.status_id= 6 and issues.assigned_to_id is null) as unassigned_rejected_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString()
|
|
+ ") and issues.status_id= 7 and issues.assigned_to_id is null) as unassigned_confirmed_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString()
|
|
+ ") and issues.status_id=15 and issues.assigned_to_id is null) as unassigned_maybe_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString()
|
|
+ ") and issues.status_id=16 and issues.assigned_to_id is null) as unassigned_waiting_issue_count, ");
|
|
sql.append(" (select min(created_on) from issues where issues.project_id in (" + subSql.toString() + ")) as first_issue_create, ");
|
|
sql.append(" (select max(updated_on) from issues where issues.project_id in (" + subSql.toString() + ")) as last_issue_update ");
|
|
|
|
sql.append("FROM ");
|
|
sql.append(" projects as currentProject ");
|
|
sql.append("WHERE ");
|
|
sql.append(" currentProject.status=1 and currentProject.is_public=1;");
|
|
|
|
// System.out.println(sql.toStringSeparatedBy("\n"));
|
|
|
|
this.connection.setAutoCommit(true);
|
|
statement = this.connection.prepareStatement(sql.toString());
|
|
resultSet = statement.executeQuery();
|
|
|
|
while (resultSet.next())
|
|
{
|
|
long id = resultSet.getLong(1);
|
|
String identifier = resultSet.getString(2);
|
|
String name = resultSet.getString(3);
|
|
Long parentId = SQLUtils.getNullableLong(resultSet, 4);
|
|
|
|
Project project = new Project(id, identifier, name, parentId);
|
|
project.setChildCount(resultSet.getLong(5));
|
|
project.setLastUpdate(SQLUtils.toLocalDateTime(resultSet.getTimestamp(6)));
|
|
|
|
project.issueStats().setCount(resultSet.getLong(7));
|
|
project.issueStats().setNewCount(resultSet.getLong(8));
|
|
project.issueStats().setOngoingCount(resultSet.getLong(9));
|
|
project.issueStats().setResolvedCount(resultSet.getLong(10));
|
|
project.issueStats().setClosedCount(resultSet.getLong(11));
|
|
project.issueStats().setRejectedCount(resultSet.getLong(12));
|
|
project.issueStats().setConfirmedCount(resultSet.getLong(13));
|
|
project.issueStats().setMaybeCount(resultSet.getLong(14));
|
|
project.issueStats().setWaitingCount(resultSet.getLong(15));
|
|
|
|
project.issueStats().setUnassignedCount(resultSet.getLong(16));
|
|
project.issueStats().setUnassignedNewCount(resultSet.getLong(17));
|
|
project.issueStats().setUnassignedOngoingCount(resultSet.getLong(18));
|
|
project.issueStats().setUnassignedResolvedCount(resultSet.getLong(19));
|
|
project.issueStats().setUnassignedClosedCount(resultSet.getLong(20));
|
|
project.issueStats().setUnassignedRejectedCount(resultSet.getLong(21));
|
|
project.issueStats().setUnassignedConfirmedCount(resultSet.getLong(22));
|
|
project.issueStats().setUnassignedMaybeCount(resultSet.getLong(23));
|
|
project.issueStats().setUnassignedWaitingCount(resultSet.getLong(24));
|
|
|
|
project.issueStats().setFirstCreate(SQLUtils.toLocalDateTime(resultSet.getTimestamp(25)));
|
|
project.issueStats().setLastUpdate(SQLUtils.toLocalDateTime(resultSet.getTimestamp(26)));
|
|
|
|
result.add(project);
|
|
}
|
|
}
|
|
catch (SQLException exception)
|
|
{
|
|
throw new AgirStatoolException("Error reading projects extended: " + exception.getMessage(), exception);
|
|
}
|
|
finally
|
|
{
|
|
SQLUtils.closeQuietly(statement, resultSet);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* List projects consolidated.
|
|
*
|
|
* @return the projects
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public Projects fetchProjectsWithSubStats() throws AgirStatoolException
|
|
{
|
|
Projects result;
|
|
|
|
result = fetchProjectsWithStats(Project.Type.CONSOLIDATED);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Fetch week concluded count.
|
|
*
|
|
* @param project
|
|
* the project
|
|
* @return the date count map
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public DateCountMap fetchWeekClosedOnCount(final Project project) throws AgirStatoolException
|
|
{
|
|
DateCountMap result;
|
|
|
|
result = new DateCountMap();
|
|
|
|
//
|
|
PreparedStatement statement = null;
|
|
ResultSet resultSet = null;
|
|
try
|
|
{
|
|
StringList subSql = new StringList();
|
|
if (project.getType() == Project.Type.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 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());
|
|
}
|
|
|
|
StringList sql = new StringList();
|
|
sql.append("SELECT");
|
|
sql.append(" yearweek(closed_on, 3) 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
|
|
* @return the date count map
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public DateCountMap fetchWeekCreatedCount(final Project project) throws AgirStatoolException
|
|
{
|
|
DateCountMap result;
|
|
|
|
result = new DateCountMap();
|
|
|
|
//
|
|
PreparedStatement statement = null;
|
|
ResultSet resultSet = null;
|
|
try
|
|
{
|
|
StringList subSql = new StringList();
|
|
if (project.getType() == Project.Type.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 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());
|
|
}
|
|
|
|
StringList sql = new StringList();
|
|
sql.append("SELECT ");
|
|
sql.append(" yearweek(created_on,3) 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 root project.
|
|
*
|
|
* @return the root project
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public Project getRootProject() throws AgirStatoolException
|
|
{
|
|
Project result;
|
|
|
|
result = null;
|
|
PreparedStatement statement = null;
|
|
ResultSet resultSet = null;
|
|
try
|
|
{
|
|
StringList subSql = new StringList();
|
|
subSql.append("select ");
|
|
subSql.append(" id ");
|
|
subSql.append("from ");
|
|
subSql.append(" projects as childProjects ");
|
|
subSql.append("where ");
|
|
subSql.append(" childProjects.status=1 and childProjects.is_public=1");
|
|
|
|
//
|
|
StringList sql = new StringList();
|
|
sql.append("SELECT");
|
|
sql.append(" 0 as root_project_id,");
|
|
sql.append(" 'all' as root_project_identifier,");
|
|
sql.append(" '*' as root_project_name,");
|
|
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 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() + ") and issues.status_id= 1) as new_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 2) as ongoing_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 3) as resolved_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 5) as closed_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 6) as rejected_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id= 7) as confirmed_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id=15) as maybe_issue_count,");
|
|
sql.append(" (select count(*) from issues where issues.project_id in (" + subSql.toString() + ") and issues.status_id=16) as waiting_issue_count, ");
|
|
sql.append(" (select min(created_on) from issues where issues.project_id in (" + subSql.toString() + ")) as first_issue_create, ");
|
|
sql.append(" (select max(updated_on) from issues where issues.project_id in (" + subSql.toString() + ")) as last_issue_update ");
|
|
|
|
// System.out.println(sql.toStringSeparatedBy("\n"));
|
|
|
|
this.connection.setAutoCommit(true);
|
|
statement = this.connection.prepareStatement(sql.toString());
|
|
resultSet = statement.executeQuery();
|
|
|
|
while (resultSet.next())
|
|
{
|
|
long id = resultSet.getLong(1);
|
|
String identifier = resultSet.getString(2);
|
|
String name = resultSet.getString(3);
|
|
Long parentId = SQLUtils.getNullableLong(resultSet, 4);
|
|
|
|
result = new Project(id, identifier, name, parentId);
|
|
result.setChildCount(resultSet.getLong(5));
|
|
result.setLastUpdate(SQLUtils.toLocalDateTime(resultSet.getTimestamp(6)));
|
|
result.issueStats().setCount(resultSet.getLong(7));
|
|
result.issueStats().setNewCount(resultSet.getLong(8));
|
|
result.issueStats().setOngoingCount(resultSet.getLong(9));
|
|
result.issueStats().setResolvedCount(resultSet.getLong(10));
|
|
result.issueStats().setClosedCount(resultSet.getLong(11));
|
|
result.issueStats().setRejectedCount(resultSet.getLong(12));
|
|
result.issueStats().setConfirmedCount(resultSet.getLong(13));
|
|
result.issueStats().setMaybeCount(resultSet.getLong(14));
|
|
result.issueStats().setWaitingCount(resultSet.getLong(15));
|
|
result.issueStats().setFirstCreate(SQLUtils.toLocalDateTime(resultSet.getTimestamp(16)));
|
|
result.issueStats().setLastUpdate(SQLUtils.toLocalDateTime(resultSet.getTimestamp(17)));
|
|
}
|
|
}
|
|
catch (SQLException exception)
|
|
{
|
|
throw new AgirStatoolException("Error reading projects extended: " + exception.getMessage(), exception);
|
|
}
|
|
finally
|
|
{
|
|
SQLUtils.closeQuietly(statement, resultSet);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks for to update.
|
|
*
|
|
* @param project
|
|
* the project
|
|
* @return true, if successful
|
|
*/
|
|
public boolean hasToUpdate(final Project project)
|
|
{
|
|
boolean result;
|
|
|
|
if (project == null)
|
|
{
|
|
result = false;
|
|
}
|
|
else
|
|
{
|
|
File target = new File(this.targetDirectory, project.getPath());
|
|
if (target.exists())
|
|
{
|
|
if (project.hasIssue())
|
|
{
|
|
LocalDateTime lastFileUpdate = LocalDateTime.ofEpochSecond(target.lastModified() / 1000, 0, ZoneOffset.UTC);
|
|
|
|
if (project.issueStats().getLastUpdate().isAfter(lastFileUpdate))
|
|
{
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* List projects.
|
|
*
|
|
* @return the projects
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public Projects listProjects() throws AgirStatoolException
|
|
{
|
|
Projects result;
|
|
|
|
result = new Projects();
|
|
|
|
//
|
|
PreparedStatement statement = null;
|
|
ResultSet resultSet = null;
|
|
try
|
|
{
|
|
StringList sql = new StringList();
|
|
sql.append("SELECT ");
|
|
sql.append(" id,");
|
|
sql.append(" identifier,");
|
|
sql.append(" name,");
|
|
sql.append(" parent_id,");
|
|
sql.append(" ( ");
|
|
sql.append(" select ");
|
|
sql.append(" count(*) ");
|
|
sql.append(" from projects as subprojects ");
|
|
sql.append(" where ");
|
|
sql.append(" subprojects.parent_id = projects.id ");
|
|
sql.append(" and subprojects.status = 1 ");
|
|
sql.append(" and subprojects.is_public = 1 ");
|
|
sql.append(" ) as child_count, ");
|
|
sql.append(" updated_on ");
|
|
sql.append("from ");
|
|
sql.append(" projects ");
|
|
sql.append("where ");
|
|
sql.append(" status=1 and is_public=1");
|
|
|
|
// System.out.println(sql.toStringSeparatedBy("\n"));
|
|
|
|
this.connection.setAutoCommit(true);
|
|
statement = this.connection.prepareStatement(sql.toString());
|
|
resultSet = statement.executeQuery();
|
|
|
|
while (resultSet.next())
|
|
{
|
|
long id = resultSet.getLong(1);
|
|
String identifier = resultSet.getString(2);
|
|
String name = resultSet.getString(3);
|
|
Long parentId = SQLUtils.getNullableLong(resultSet, 4);
|
|
|
|
Project project = new Project(id, identifier, name, parentId);
|
|
project.setChildCount(resultSet.getLong(5));
|
|
project.setLastUpdate(SQLUtils.toLocalDateTime(resultSet.getTimestamp(6)));
|
|
|
|
result.add(project);
|
|
}
|
|
}
|
|
catch (SQLException exception)
|
|
{
|
|
logger.error("Error getting element.", exception);
|
|
throw new AgirStatoolException("Error getting element", exception);
|
|
}
|
|
finally
|
|
{
|
|
SQLUtils.closeQuietly(statement, resultSet);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* List projects as tree.
|
|
*
|
|
* @return the project
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public Project listProjectsAsTree() throws AgirStatoolException
|
|
{
|
|
Project result;
|
|
|
|
// Create a root project.
|
|
result = getRootProject();
|
|
|
|
//
|
|
Projects projects = fetchProjectsWithSubStats();
|
|
|
|
// Add parent projects with alone statistics.
|
|
for (Project project : fetchProjectsWithoutSubStats())
|
|
{
|
|
if (project.hasChild())
|
|
{
|
|
project.setName(ALONE_INDICATOR + project.getName());
|
|
project.setIdentifier(ALONE_INDICATOR + project.getIdentifier());
|
|
project.setParentId(project.getId());
|
|
project.setChildCount(0);
|
|
projects.add(project);
|
|
}
|
|
}
|
|
|
|
// Fill created and concluded issues history.
|
|
{
|
|
{
|
|
Issues issues = fetchIssue(result);
|
|
result.issues().addAll(issues);
|
|
}
|
|
{
|
|
DateCountMap map = fetchWeekCreatedCount(result);
|
|
DateCountList counts = AgirStatoolUtils.normalizedWeekCountList(map, result.issueStats().getFirstCreate().toLocalDate());
|
|
result.issueStats().setWeekCreatedIssueCounts(counts);
|
|
}
|
|
{
|
|
DateCountMap map = fetchWeekClosedOnCount(result);
|
|
DateCountList counts = AgirStatoolUtils.normalizedWeekCountList(map, result.issueStats().getFirstCreate().toLocalDate());
|
|
result.issueStats().setWeekConcludedIssueCounts(counts);
|
|
}
|
|
}
|
|
for (Project project : projects)
|
|
{
|
|
logger.debug("Fetching Created/Closed history for " + project.getName());
|
|
if (project.hasIssue())
|
|
{
|
|
{
|
|
Issues issues = fetchIssue(project);
|
|
project.issues().addAll(issues);
|
|
}
|
|
{
|
|
DateCountMap map = fetchWeekCreatedCount(project);
|
|
DateCountList counts = AgirStatoolUtils.normalizedWeekCountList(map, project.issueStats().getFirstCreate().toLocalDate());
|
|
project.issueStats().setWeekCreatedIssueCounts(counts);
|
|
}
|
|
{
|
|
DateCountMap map = fetchWeekClosedOnCount(project);
|
|
DateCountList counts = AgirStatoolUtils.normalizedWeekCountList(map, project.issueStats().getFirstCreate().toLocalDate());
|
|
project.issueStats().setWeekConcludedIssueCounts(counts);
|
|
}
|
|
}
|
|
}
|
|
logger.debug("Fetching Created/Closed history done.");
|
|
|
|
// Transform as tree.
|
|
for (Project project : projects)
|
|
{
|
|
if ((project.getParentId() == null) || (project.getId() == 0))
|
|
{
|
|
result.subProjects().add(project);
|
|
|
|
Projects subProjects = projects.getByParent(project.getId());
|
|
subProjects.sortByName();
|
|
project.subProjects().addAll(subProjects);
|
|
}
|
|
}
|
|
|
|
result.subProjects().sortByName();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
public void putTouchFile()
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* Refresh.
|
|
*
|
|
* @param projectId
|
|
* the project id
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public void refresh(final String projectId) throws AgirStatoolException
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Update page.
|
|
*
|
|
* @param project
|
|
* the project
|
|
* @throws AgirStatoolException
|
|
* the agir statool exception
|
|
*/
|
|
public void updatePage(final Project project) throws AgirStatoolException
|
|
{
|
|
try
|
|
{
|
|
if (hasToUpdate(project))
|
|
{
|
|
logger.info("Refresh project page for {}", project.getName());
|
|
String page = ProjectPage.build(project);
|
|
FileUtils.write(new File(this.targetDirectory, project.getIdentifier() + ".xhtml"), page, StandardCharsets.UTF_8);
|
|
}
|
|
}
|
|
catch (IOException exception)
|
|
{
|
|
throw new AgirStatoolException("Error refreshing page: " + exception.getMessage(), exception);
|
|
}
|
|
}
|
|
}
|