Project Reports & Websites

Steven J Zeil

Last modified: Mar 22, 2024
Contents:

Abstract

In this lesson we will continue looking at project documentation:

We will look at how to use our automated build tools to keep project reporting up-to-date.

1 Project Reports

1.1 Test Reports

We’ve already looked at JUnit, which can be used to generate test reports like this one or this one.

This is generated in Gradle via the java plugin. It produces the web reports in build\reports\tests.

1.2 Static Code Analyzers

Many tools that we will cover later for analyzing code can produce useful (or at least, impressive) documentation as a side effect.

1.3 Configuration Reports

Configuration managers generate reports about the dependencies among the software components.

For example, this report comes from Gradle by adding

plugins {
     ⋮
   id 'project-report'
     ⋮
}

to the build.xml file

2 Project Websites

2.1 Case study: Constructing a project website with Gradle

Let’s look at the process of building a simple website that provides

  1. A brief project description
  2. Links to (and copies of)

We can consider adding other content in later lessons.

2.1.1 Build plan

After a build, we can construct a website in build/reports by

  1. Copying build/doc/ into build/reports

    • This gets a copy of the Javadocs

    • Note that the JUnit and project configuration reports will already be in subdirectories of build/reports.

  2. Copying HTML, CSS, and Javascript files from src/main/html into reports.

    • Hand-crafted pages with the project description and links to the three reports.

2.1.2 The HTML File

home_html.listing
<!DOCTYPE html>
<html>
	<head>
	    <title>Project Reports</title>
	    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
	    <link rel="stylesheet" type="text/css" href="projectReports.css"/>
	    <script src="projectReports.js"></script>
	</head>
	<body>
	    <h1>CodeCompCommon</h1>

        <p>This project provides a common set of interfaces for all CS350
           CodeComp project variants.</p>

        <p>It also provides a SharedPhrases class used to store token lists
           obtained via lexical analysis of the source code.</p>

        <h2>Information</h2>
		
        <p>Current version: 1.3</p>
        
	    <h2>Detailed Reports</h2>
		
	    <div class="reportLinks">
	   		<ul>
	   		    <li>General:
	   		        <ul>
	   			       <li><a href="../docs/javadoc/">JavaDoc</a></li>
	   			       <li><a href="../project/dependencies/root.html">Project Dependencies</a></li>
	   			    </ul></li>
	   			<li>Tests:
	   			    <ul>
	   			        <li><a href="../tests/test/">JUnit tests</a></li>
                    </ul>					
				</li>	    			
	   		</ul>
	   	</div>
	    
	</body>
</html>

2.1.3 Gradle setup

After a build, we can construct a website in build/reports by

  1. Copying build/doc/ into build/website
  2. Copying HTML, CSS, and Javascript files from src/main/html into reports.
    test.ignoreFailures=true                ➀
    check.dependsOn htmlDependencyReport    ➁
    
    task copyWebPages (type: Copy) {        ➄
        from 'src/main/html'
        into 'build/reports'
    }
    
    task copyDocs (type: Copy) {            ➃
        from 'build/docs'
        into 'build/reports/docs'
        dependsOn 'javadoc'
    }
    
    task buildSite (dependsOn: ['javadoc', 'check',             ➂
                          'copyWebPages', 'copyDocs']) {
        description 'Generate reports website for this project'
    }
    

2.2 Case study: Constructing a project website with Gradle and Jbake

Example

For a more elaborate, multi-page site, we can use a static site generator to make it easy to enforce a common look-and-feel across pages.

I’ve chose Jbake mainly because it has an easy-to-use gradle plugin.

 

JBake injects content using a series of templates

2.2.1 Gradle & Jbake

Gradle has a jbake plugin that adds a bake task to generate a website.

2.2.2 The website setup

src
|-- main
|-- |-- jbake
|-- |-- |-- assets
|-- |-- |-- |-- css
|-- |-- |-- |-- '-- base.css
|-- |-- |-- '-- js
|-- |-- |--     '-- jquery-1.11.1.min.js
|-- |-- |-- content
|-- |-- |-- |-- dependencies.html
|-- |-- |-- |-- home0.md
|-- |-- |-- |-- javadoc.html
|-- |-- |-- '-- junit.html
|-- |-- |-- jbake.properties
|-- |-- '-- templates
|-- |--     |-- footer.ftl
|-- |--     |-- header.ftl
|-- |--     |-- index.ftl
|-- |--     |-- menu.ftl
|-- |--     |-- page.ftl
|-- |--     '-- sitemap.ftl

The content directory` holds the actual information we want to disseminate.

The templates directory determines the “look and feel” of the website.

Templates:

page.ftl.listing
<#include "header.ftl">

	<div class="center">
	    <#include "menu.ftl">
        <div class="rightPart">
            <div class="page-header">
                <h1><#escape x as x?xml>${content.title}</#escape></h1>
            </div>
            <p><em>${content.date?string("dd MMMM yyyy")}</em></p>
			<p>${content.body}</p>
		</div>
	</div>
	<hr />

<#include "footer.ftl">
header.ftl.listing
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title><#if (content.title)??><#escape x as x?xml>${content.title}</#escape><#else>codecentric</#if></title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="">
    <meta name="keywords" content="">
    <meta name="generator" content="JBake">

    <link href="<#if (content.rootpath)??>${content.rootpath}<#else></#if>css/base.css" rel="stylesheet">
    <link href="<#if (content.rootpath)??>${content.rootpath}<#else></#if>css/projectReports.css" rel="stylesheet">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js" type="text/javascript"></script>
    <script src="https://code.highcharts.com/highcharts.js"></script>
    <script src="https://code.highcharts.com/modules/data.js"></script>
    <script src="<#if (content.rootpath)??>${content.rootpath}<#else></#if>js/projectReports.js"></script>


  </head>
  <body>
    <div id="mainBody">
   
menu.ftl.listing
<div class="leftPart">
  <div class="menuBlock">
    <span class="menuBlockHeader"><a href="<#if
										   (content.rootpath)??>${content.rootpath}<#else></#if>index.html">Home</a></span>
  </div>
  <div class="menuBlock">
    <span class="menuBlockHeader">Project Info</span>
	<ul>
      <li><a href="<#if (content.rootpath)??>${content.rootpath}<#else></#if>javadoc.html">API (Javadoc)</a></li>
      <li><a href="<#if (content.rootpath)??>${content.rootpath}<#else></#if>dependencies.html">Dependencies</a></li>
	</ul>
  </div>
  <div class="menuBlock">
    <span class="menuBlockHeader">Testing</span>
	<ul>
      <li><a href="<#if
      (content.rootpath)??>${content.rootpath}<#else></#if>junit.html">Unit
		  Tests</a></li>
	</ul>
  </div>
  <div class="menuBlock">
 	<span class="menuBlockHeader">Analysis Reports</span>
	<ul>
      <li><a href="<#if (content.rootpath)??>${content.rootpath}<#else></#if>checkstyle.html">Checkstyle</a></li>
      <li><a href="<#if (content.rootpath)??>${content.rootpath}<#else></#if>spotbugs.html">SpotBugs</a></li>
	</ul>
  </div>
</div>
footer.ftl.listing
    </div>
    
    <div class="footer">
    &copy; 2020 Old Dominion University
    </div>
        
  </body>
</html>

Content:

junit.html.listing
title=code-grader Unit Tests
type=page
status=published
~~~~~~
</p>
<div class=reportGraphs>
   <div id="junitGraph" class="graph">Test Results</div>
</div>

<iframe class="docFrame" src="tests/test/index.html"> </iframe>

<script type="text/javascript">
   register2("junitGraph", "tests.csv", "JUnit Tests", "Test Cases");
</script>

<p>
home0.md.listing
title=Project Documentation: code grader
date=2024-02-18
type=page
status=published
~~~~~~

2.2.3 Build Steps

To generate a website in build/jbake

  1. Copy the build/reports and build/docs to build/tmp/website
  2. Copy src/jbake to build/tmp/website
  3. Concatenate build/tmp/website/content/home0.md and the project README.md to form build/tmp/website/content/home.md
  4. Run Jbake on build/tmp/website/content/home.md to generate the full website in build/jbake

In build.gradle:

build.gradle.jbake.listing
test.ignoreFailures=true
check.dependsOn htmlDependencyReport

task reports (dependsOn: ['javadoc', 'check']) {
    description 'Generate all reports for this project'
}


task copyJDocs (type: Copy) {
    from 'build/docs'
    into 'build/tmp/website/assets'
    dependsOn 'javadoc'
}

task copyReports (type: Copy) {
    from 'build/reports'
    into 'build/tmp/website/assets'
    dependsOn 'reports'
}

task copyJbakeTemplates (type: Copy) {
    from 'src/jbake'
    into 'build/tmp/website'
}

task buildHomePage (dependsOn: copyJbakeTemplates) {
	 inputs.files ( 'build/tmp/website/content/home0.md', '../README.md')
	 outputs.file ( 'build/tmp/website/content/home.md' )
	 doLast  {
	     outputs.files.singleFile.withOutputStream { out ->
	         for ( file in inputs.files ) file.withInputStream {
		 	     out << it << '\n' }
	    }
	 }
}

jbake {
	 srcDirName = "build/tmp/website"
}

task setupWebsite (dependsOn: ['buildHomePage', 'copyReports', 'copyJDocs']){
}

bake.dependsOn "setupWebsite"

3 Case Study: Enhanced reporting

Goal: add project website pages with reports from analysis tools and trend information (historical graphs) similar to those offered by Jenkins.

3.1 Generating Graphs

Highcharts is a Javascript package that can generate plots from data captured in CSV (Comma-Separated-Values) format.

Highcharts requires a bit of Javascript to inject a chart into an HTML div element.

3.2 Generating the Data

Where does the data for the plots come from?

3.3 Report Accumulator

The reportAccumulator plugin is currently distributed from my own repository. So, in settings.gradle:

pluginManagement {
    repositories {
        ivy { // Use my own CS dept repo
            url 'https://www.cs.odu.edu/~zeil/ivyrepo' // My binary repository
        }
        gradlePluginPortal()
        mavenCentral()
    }
}

Then back to build.gradle to load another plugin:

plugins {
    ⋮
    id 'edu.odu.cs.report_accumulator' version '1.4'  ➀
}

    ⋮

// Reporting

reportStats {
    reportsURL = 'https://www.cs.odu.edu/~zeil/gitlab/codeCompCommon/'  ➁
}

reportStats.dependsOn check  ➂

task deployReports       ➃
   ⋮
}

deployReports.dependsOn  reportStats  ➄

See the documentation on the reportAccumulator plugin for more details.

3.4 Forges

A software forge is a collection of web services for the support of collaborative software devlopment:


Forge Examples

Among the best known forges are