All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.myire.quill.dashboard.DashboardTask Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015, 2019, 2021 Peter Franzen. All rights reserved.
 *
 * Licensed under the Apache License v2.0: http://www.apache.org/licenses/LICENSE-2.0
 */
package org.myire.quill.dashboard;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import groovy.lang.Closure;

import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.Task;
import org.gradle.api.reporting.Report;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.TaskAction;
import org.gradle.util.ConfigureUtil;

import org.myire.quill.common.Projects;
import org.myire.quill.common.Tasks;
import org.myire.quill.report.ReportBuilder;
import org.myire.quill.report.ReportingEntity;
import org.myire.quill.report.Reports;


/**
 * A task that creates a summary report of a collection of reports generated during a build. The
 * summary report consists of one {@code DashboardSection} for each report included in the summary.
 *

* By default the task generates a summary of the reports for the tasks represented by the sections * returned by {@link DashboardSectionFactory#createAvailableDefaultSections()}. Build scripts can * access and modify these standard sections through the {@code sections} property, which is a map * from task name to {@code DashboardSection} instance. New sections can also be added to this map. *

* The layout of the dashboard sections can be configured through the task's {@code layout} * property. */ public class DashboardTask extends DefaultTask implements ReportingEntity { static final String TASK_NAME = "reportsDashboard"; private boolean fVerbose = true; private DashboardLayout fLayout; private DashboardReports fReports; private Map fSections = new LinkedHashMap<>(); private boolean fHasAddedDefaultSections; private DashboardSectionFactory fSectionFactory; /** * Add the default sections before configuring the task. * * @param pClosure The closure to configure the task with. * * @return This task. */ @Override public Task configure(Closure pClosure) { maybeAddDefaultSections(); return super.configure(pClosure); } /** * Should the path to the report file be logged after it has been created? * * @return True if the path should be logged, false if not. */ @Input public boolean isVerbose() { return fVerbose; } public void setVerbose(boolean pVerbose) { fVerbose = pVerbose; } /** * Get the layout of the dashboard. * * @return The dashboard's layout. */ @Nested public DashboardLayout getLayout() { return fLayout; } /** * Configure the dashboard's layout. * * @param pClosure A closure that configures the layout. * * @return The dashboard's layout. */ public DashboardLayout layout(Closure pClosure) { return ConfigureUtil.configureSelf(pClosure, fLayout); } /** * Get the reports produced by this task. * * @return The reports. */ @Override @Nested public DashboardReports getReports() { return fReports; } /** * Configure this task's reports. * * @param pClosure A closure that configures the reports. * * @return This task's reports. */ @Override public DashboardReports reports(Closure pClosure) { fReports.configure(pClosure); return fReports; } /** * Configure this task's reports. * * @param pAction An action that configures the reports. * * @return This task's reports. */ @Override public DashboardReports reports(Action pAction) { pAction.execute(fReports); return fReports; } /** * Get the map with the individual dashboard sections that will be included in the report. * * @return The report's sections. */ @Input public List getInputSections() { maybeAddDefaultSections(); return fSections.values() .stream() .map(DashboardSectionSpec::new) .collect(Collectors.toList()); } /** * Get the map with the individual dashboard sections that will be included in the report. * * @return The report's sections. */ @Internal public Map getSections() { maybeAddDefaultSections(); return fSections; } /** * Add or modify the dashboard section with a specific name. * * @param pName The name of the dashboard section. * @param pReport The XML report to transform. * @param pXslFile The XSL file to transform the report with. */ public void addSection(String pName, Report pReport, Object pXslFile) { addSection(pName, pReport, null, pXslFile); } /** * Add or modify the dashboard section with a specific name. * * @param pName The name of the dashboard section. * @param pReport The XML report to transform. * @param pDetailedReport The detailed report to refer to from the dashboard section. * @param pXslFile The XSL file to transform the report with. */ public void addSection(String pName, Report pReport, Report pDetailedReport, Object pXslFile) { File aXslFile = getProject().file(pXslFile); fSections.put(pName, new DashboardSection(getProject(), pName, pReport, pDetailedReport, aXslFile)); } /** * Add a built-in dashboard section for a task. The XSL used for transforming the task's * report(s) will be the XSL resource bundled with the Quill distribution.. * * @param pTask The task to add a dashboard section for. * * @return True if a built-in section was added, false if there is no built-in section for the * task's type. */ public boolean addBuiltInSection(Task pTask) { DashboardSection aSection = fSectionFactory.create(pTask); if (aSection != null) { fSections.put(aSection.getName(), aSection); return true; } else return false; } /** * Remove a dashboard section with a specific name. * * @param pName The name of the dashboard section. * * @return The removed section, or null if there was no section with the specified name. */ public DashboardSection removeSection(String pName) { return fSections.remove(pName); } /** * Create the dashboard report with all sections that have been added to the task. */ @TaskAction public void createReport() { if (Reports.isRequired(fReports.getHtml())) { try { ReportBuilder aReportBuilder = new ReportBuilder(Reports.getOutputLocation(fReports.getHtml())); fLayout.write(aReportBuilder, fSections.values(), findChildProjectDashboards()); aReportBuilder.close(); if (fVerbose) getLogger().lifecycle( "Created reports dashboard {}", aReportBuilder.getDestination().getAbsolutePath()); } catch (IOException ioe) { getLogger().error("Could not create reports dashboard", ioe); } } } /** * Initialize the task. */ void init() { fSectionFactory = new DashboardSectionFactory(getProject()); fLayout = new DashboardLayout(getProject()); // Create the task's report container and add the container's report's destination as output // of this task. fReports = new DashboardReportsImpl(this); Tasks.outputFile(this, () -> Reports.getOutputLocation(this.getReports().getHtml())); // Only execute the task if its HTML report is enabled. onlyIf(ignore -> Reports.isRequired(getReports().getHtml())); } /** * Add all available default dashboard sections to the task if that hasn't been done. */ private void maybeAddDefaultSections() { if (fHasAddedDefaultSections) return; for (DashboardSection aSection : fSectionFactory.createAvailableDefaultSections()) { getLogger().debug("Adding default reports dashboard section {}", aSection.getName()); fSections.put(aSection.getName(), aSection); } fHasAddedDefaultSections = true; } /** * Find all child projects that have a {@code DashboardTask} and return a mapping from the * project name to the path of the dashboard report. * * @return The child projects with dashboard reports, or an empty map if the task's project has * no child projects with a {@code DashboardTask}. */ private Map findChildProjectDashboards() { // Get the DashboardTask for all child projects of this task's project. Collection aChildTasks = getProject() .getChildProjects() .values() .stream() .map(p -> Projects.getTask(p, TASK_NAME, DashboardTask.class)) .filter(Objects::nonNull) .collect(Collectors.toList()); if (aChildTasks.isEmpty()) return Collections.emptyMap(); // Create a map from child project name to dashboard report file path. Map aChildProjectsDashboards = new HashMap<>(); Path aBasePath = Reports.getOutputLocation(getReports().getHtml()).toPath().getParent(); for (DashboardTask aChildTask : aChildTasks) { Path aChildReportPath = Reports.getOutputLocation(aChildTask.getReports().getHtml()).toPath(); String aRelativePath = aBasePath.relativize(aChildReportPath).toString(); aChildProjectsDashboards.put(aChildTask.getProject().getName(), aRelativePath); } return aChildProjectsDashboards; } /** * A snapshot of a {@code DashboardSection} that can be serialized and is suitable for a task's * input. Some {@code DashboardSection} instances are not serializable due to some * {@code Report} implementations not being serializable (e.g. * org.gradle.api.reporting.internal.SimpleReport, which is the base class of many standard * reports, has some PropertyState instance variables that aren't serializable). */ static private class DashboardSectionSpec implements Serializable { static private final long serialVersionUID = 1L; private final String fName; private final DashboardSectionFile fInputReportFile; private final DashboardSectionFile fDetailedReportFile; private final DashboardSectionFile fXslFile; DashboardSectionSpec(DashboardSection pSection) { fName = pSection.getName(); fInputReportFile = toDashboardSectionFile(getReportFileSpec(pSection.getReport())); fDetailedReportFile = toDashboardSectionFile(getReportFileSpec(pSection.getDetailedReport())); fXslFile = toDashboardSectionFile(pSection.getXslFile()); } @Override public boolean equals(Object pObject) { if (pObject == this) return true; if (!(pObject instanceof DashboardSectionSpec)) return false; DashboardSectionSpec aOther = (DashboardSectionSpec) pObject; return fName.equals(aOther.fName) && Objects.equals(fInputReportFile, aOther.fInputReportFile) && Objects.equals(fDetailedReportFile, aOther.fDetailedReportFile) && Objects.equals(fXslFile, aOther.fXslFile); } @Override public int hashCode() { return Objects.hash(fName, fInputReportFile, fDetailedReportFile, fXslFile); } static private File getReportFileSpec(Report pReport) { return Reports.isRequired(pReport) ? Reports.getOutputLocation(pReport) : null; } static private DashboardSectionFile toDashboardSectionFile(File pFile) { return pFile != null ? new DashboardSectionFile(pFile) : null; } } /** * A snapshot of a {@code File} that is suitable for a task's input. */ static private class DashboardSectionFile implements Serializable { static private final long serialVersionUID = 1L; private final String fAbsolutePath; private final boolean fExists; private final long fLastModified; DashboardSectionFile(File pFile) { fAbsolutePath = pFile.getAbsolutePath(); fExists = pFile.exists(); fLastModified = pFile.lastModified(); } @Override public boolean equals(Object pObject) { if (pObject== this) return true; if (!(pObject instanceof DashboardSectionFile)) return false; DashboardSectionFile aOther = (DashboardSectionFile) pObject; return Objects.equals(fAbsolutePath, aOther.fAbsolutePath) && fExists == aOther.fExists && fLastModified == aOther.fLastModified; } @Override public int hashCode() { return Objects.hash(fAbsolutePath, Boolean.valueOf(fExists), fLastModified); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy