/* * 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; import java.io.File; 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.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 = listProjectsWithoutSubStats().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 { // TODO: add filter on .xhtml and .css file. for (File file : this.targetDirectory.listFiles()) { 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 doRefreshPages() throws AgirStatoolException { try { // Copy CSS file. 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. refreshPage(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()) { refreshPage(project); for (Project subProject : project.subProjects()) { refreshPage(subProject); // if (project.getName().equals("Chapril")) // { // System.exit(0); // } } } } catch (IOException exception) { throw new AgirStatoolException("Error refreshing all: " + exception.getMessage(), exception); } } /** * Fetch week concluded count. * * @param project * the project * @return the date count map * @throws AgirStatoolException * the agir statool exception */ public DateCountMap fetchWeekConcludedCount(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(AgirStatoolUtils.toLocaleDateTime(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(AgirStatoolUtils.toLocaleDateTime(resultSet.getTimestamp(16))); result.issueStats().setLastUpdate(AgirStatoolUtils.toLocaleDateTime(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 refresh. * * @param project * the project * @return true, if successful */ public boolean hasToRefresh(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(AgirStatoolUtils.toLocaleDateTime(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 = listProjectsWithSubStats(); // Add parent projects with alone statistics. for (Project project : listProjectsWithoutSubStats()) { 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. { { 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.debug("Fetching Created/Closed history for " + project.getName()); if (project.hasIssue()) { { DateCountMap map = fetchWeekCreatedCount(project); DateCountList counts = AgirStatoolUtils.normalizedWeekCountList(map, project.issueStats().getFirstCreate().toLocalDate()); project.issueStats().setWeekCreatedIssueCounts(counts); } { DateCountMap map = fetchWeekConcludedCount(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; } /** * List projects extended. * * @return the projects * @throws AgirStatoolException * the agir statool exception */ public Projects listProjectsWithoutSubStats() throws AgirStatoolException { Projects result; result = listProjectsWithStats(Project.Type.ALONE); // return result; } /** * List projects with stats. * * @param type * the type * @return the projects * @throws AgirStatoolException * the agir statool exception */ public Projects listProjectsWithStats(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(AgirStatoolUtils.toLocaleDateTime(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(AgirStatoolUtils.toLocaleDateTime(resultSet.getTimestamp(25))); project.issueStats().setLastUpdate(AgirStatoolUtils.toLocaleDateTime(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 listProjectsWithSubStats() throws AgirStatoolException { Projects result; result = listProjectsWithStats(Project.Type.CONSOLIDATED); // 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. */ public void refreshChangedProjects() { } /** * Refresh page. * * @param project * the project * @throws AgirStatoolException * the agir statool exception */ public void refreshPage(final Project project) throws AgirStatoolException { try { if (hasToRefresh(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); } } }