diff --git a/.vscode/launch.json b/.vscode/launch.json
index 94799b60..e77f8d67 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -23,6 +23,7 @@
"${workspaceFolder}/reporter/plaintext/src",
"${workspaceFolder}/reporter/html/src",
"${workspaceFolder}/reporter/aspec/src",
+ "${workspaceFolder}/reporter/ux/src",
"${workspaceFolder}/product/src/test/java",
"${workspaceFolder}/api/src",
"${workspaceFolder}/exporter/specobject/src",
diff --git a/README.md b/README.md
index 10af627d..3d62aef1 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@ Below you see a screenshot of an HTML tracing report where OFT traces itself. Yo
+In addition to the HTML tracing report an interactive requirement browser and analysis tool is integrated into OpenFastTrace.
+
## Project Information
[](https://github.com/itsallcode/openfasttrace/actions/workflows/build.yml)
diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/ReportSettings.java b/api/src/main/java/org/itsallcode/openfasttrace/api/ReportSettings.java
index b8af61b7..494dd19c 100644
--- a/api/src/main/java/org/itsallcode/openfasttrace/api/ReportSettings.java
+++ b/api/src/main/java/org/itsallcode/openfasttrace/api/ReportSettings.java
@@ -19,7 +19,12 @@ public class ReportSettings
private final ColorScheme colorScheme;
private final DetailsSectionDisplay detailsSectionDisplay;
- private ReportSettings(final Builder builder)
+ /**
+ * Settings for a reporter.
+ *
+ * @param builder builder for a reporter
+ */
+ protected ReportSettings(final Builder builder)
{
this.verbosity = builder.verbosity;
this.showOrigin = builder.showOrigin;
@@ -121,7 +126,10 @@ public static class Builder
private ReportVerbosity verbosity = ReportVerbosity.FAILURE_DETAILS;
private ColorScheme colorScheme = ColorScheme.BLACK_AND_WHITE;
- private Builder()
+ /**
+ * Create the builder
+ */
+ protected Builder()
{
// empty by intention
}
diff --git a/core/src/main/resources/usage.txt b/core/src/main/resources/usage.txt
index f1616092..90c63c2c 100644
--- a/core/src/main/resources/usage.txt
+++ b/core/src/main/resources/usage.txt
@@ -8,7 +8,7 @@ Commands:
convert Convert to a different requirements format
Tracing options:
- -o, --output-format Report format, one of "plain", "html", "aspec"
+ -o, --output-format Report format, one of "plain", "html", "aspec", "ux"
Defaults to "plain"
-v, --report-verbosity Set how verbose the output is. Ranges from
"quiet" to "all".
diff --git a/doc/user_guide.md b/doc/user_guide.md
index 89afea1f..2635ec79 100644
--- a/doc/user_guide.md
+++ b/doc/user_guide.md
@@ -462,6 +462,18 @@ While plain text reports are perfect for debugging your tracing chain, sometimes
oft trace -o html
```
+### Interactive requirement analyisis
+
+Besides a basic HTML visualization of requirements OpenFastTrace also provides an interactive requirement browsing and requirement analysis frontend in the form of a responsive HTML page similar to the HTML report.
+
+The UX reporter:
+
+```
+oft trace -o ux
+```
+
+generates an input file for the OpenFastTrace-UX HTML frontend [OpenFastTrace-UX](https://github.com/poldi2015/openfasttrace-ux).
+
### Understanding and Fixing Broken Requirement Branches
Requirements — or specification items as we call them more broadly — in OFT are internally organized in a graph. If you haven't heard of that term, don't worry. In most cases it is close enough to think of the relationships between the specification items like a forest where the highest level of the specification are tree trunks from which details branch out into big branches, twigs and eventually leaves.
@@ -568,6 +580,7 @@ One of:
* `plain`
* `html`
* `aspec`
+* `ux`
Defaults to `plain`.
diff --git a/oft-self-trace.sh b/oft-self-trace.sh
index 47b612c0..983d830a 100755
--- a/oft-self-trace.sh
+++ b/oft-self-trace.sh
@@ -27,6 +27,7 @@ if $oft_script trace \
"$base_dir/reporter/plaintext/src" \
"$base_dir/reporter/html/src" \
"$base_dir/reporter/aspec/src" \
+ "$base_dir/reporter/ux/src" \
"$base_dir/product/src/test/java" \
"$base_dir/api/src" \
"$base_dir/exporter/specobject/src" \
diff --git a/parent/pom.xml b/parent/pom.xml
index f1b712d0..51ff11e3 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -10,7 +10,7 @@
Free requirement tracking suite
https://github.com/itsallcode/openfasttrace
- 4.1.0
+ 4.2.0
17
5.11.4
3.5.2
@@ -186,6 +186,12 @@
${revision}
compile
+
+ org.itsallcode.openfasttrace
+ openfasttrace-reporter-ux
+ ${revision}
+ compile
+
org.itsallcode.openfasttrace
openfasttrace-testutil
@@ -378,7 +384,7 @@
true
true
- true
+ false
false
-html5
diff --git a/pom.xml b/pom.xml
index 1d9da986..d7c43adc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,6 +33,7 @@
reporter/plaintext
reporter/html
reporter/aspec
+ reporter/ux
testutil
diff --git a/product/pom.xml b/product/pom.xml
index ced14315..6cc121cf 100644
--- a/product/pom.xml
+++ b/product/pom.xml
@@ -57,6 +57,10 @@
org.itsallcode.openfasttrace
openfasttrace-reporter-aspec
+
+ org.itsallcode.openfasttrace
+ openfasttrace-reporter-ux
+
org.itsallcode.openfasttrace
openfasttrace-testutil
diff --git a/product/src/test/java/org/itsallcode/openfasttrace/TestAllServicesAvailable.java b/product/src/test/java/org/itsallcode/openfasttrace/TestAllServicesAvailable.java
index 33368f05..c238dc79 100644
--- a/product/src/test/java/org/itsallcode/openfasttrace/TestAllServicesAvailable.java
+++ b/product/src/test/java/org/itsallcode/openfasttrace/TestAllServicesAvailable.java
@@ -100,7 +100,7 @@ void exporterAvailable(final String format)
@ParameterizedTest
@CsvSource(
- { "aspec", "html", "plain" })
+ { "aspec", "html", "plain", "ux" })
void reporterAvailable(final String format)
{
if (!reporterLoader.isFormatSupported(format))
diff --git a/product/src/test/java/org/itsallcode/openfasttrace/core/serviceloader/TestInitializingServiceLoader.java b/product/src/test/java/org/itsallcode/openfasttrace/core/serviceloader/TestInitializingServiceLoader.java
index 63f89839..8f91bffc 100644
--- a/product/src/test/java/org/itsallcode/openfasttrace/core/serviceloader/TestInitializingServiceLoader.java
+++ b/product/src/test/java/org/itsallcode/openfasttrace/core/serviceloader/TestInitializingServiceLoader.java
@@ -21,6 +21,7 @@
import org.itsallcode.openfasttrace.report.aspec.ASpecReporterFactory;
import org.itsallcode.openfasttrace.report.html.HtmlReporterFactory;
import org.itsallcode.openfasttrace.report.plaintext.PlaintextReporterFactory;
+import org.itsallcode.openfasttrace.report.ux.UxReporterFactory;
import org.junit.jupiter.api.Test;
/**
@@ -83,9 +84,10 @@ void testReporterFactoriesRegistered()
final ReporterContext context = new ReporterContext(null);
final List services = getRegisteredServices(ReporterFactory.class,
context);
- assertThat(services, hasSize(3));
+ assertThat(services, hasSize(4));
assertThat(services, containsInAnyOrder(instanceOf(PlaintextReporterFactory.class),
instanceOf(ASpecReporterFactory.class),
+ instanceOf(UxReporterFactory.class),
instanceOf(HtmlReporterFactory.class)));
for (final ReporterFactory factory : services)
{
diff --git a/reporter/ux/pom.xml b/reporter/ux/pom.xml
new file mode 100644
index 00000000..ae01cf9f
--- /dev/null
+++ b/reporter/ux/pom.xml
@@ -0,0 +1,32 @@
+
+ 4.0.0
+ openfasttrace-reporter-ux
+ OpenFastTrace UX Reporter
+
+ ../../parent/pom.xml
+ org.itsallcode.openfasttrace
+ openfasttrace-parent
+ ${revision}
+
+
+ ${reproducible.build.timestamp}
+
+
+
+ org.itsallcode.openfasttrace
+ openfasttrace-api
+
+
+ org.itsallcode.openfasttrace
+ openfasttrace-testutil
+ test
+
+
+ org.itsallcode.openfasttrace
+ openfasttrace-core
+ test
+
+
+
\ No newline at end of file
diff --git a/reporter/ux/src/main/java/module-info.java b/reporter/ux/src/main/java/module-info.java
new file mode 100644
index 00000000..319cf608
--- /dev/null
+++ b/reporter/ux/src/main/java/module-info.java
@@ -0,0 +1,13 @@
+/**
+ * This provides an interactive HTML requirement browser.
+ *
+ * @provides org.itsallcode.openfasttrace.api.report.ReporterFactory
+ */
+module org.itsallcode.openfasttrace.report.ux
+{
+ requires transitive org.itsallcode.openfasttrace.api;
+ requires java.logging;
+
+ provides org.itsallcode.openfasttrace.api.report.ReporterFactory
+ with org.itsallcode.openfasttrace.report.ux.UxReporterFactory;
+}
diff --git a/reporter/ux/src/main/java/org/itsallcode/openfasttrace/report/ux/Collector.java b/reporter/ux/src/main/java/org/itsallcode/openfasttrace/report/ux/Collector.java
new file mode 100644
index 00000000..feaf9e03
--- /dev/null
+++ b/reporter/ux/src/main/java/org/itsallcode/openfasttrace/report/ux/Collector.java
@@ -0,0 +1,564 @@
+package org.itsallcode.openfasttrace.report.ux;
+
+import org.itsallcode.openfasttrace.api.core.*;
+import org.itsallcode.openfasttrace.report.ux.model.Coverage;
+import org.itsallcode.openfasttrace.report.ux.model.UxModel;
+import org.itsallcode.openfasttrace.report.ux.model.UxSpecItem;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Collector traverses a {@link LinkedSpecificationItem} tree and provides a {@link UxSpecItem} and
+ * a {@link UxModel} based on the parsed items.
+ */
+public class Collector {
+
+ private final List items = new ArrayList<>();
+ private final List ids = new ArrayList<>();
+
+ private final List allTypes = new ArrayList<>();
+ private final List orderedTypes = new ArrayList<>();
+
+ private final List tags = new ArrayList<>();
+ private final List tagCount = new ArrayList<>();
+
+ final List