Commit 363ca9a5 authored by Volker Krause's avatar Volker Krause
Browse files

Add documentation for telemetry data model and survey targeting

parent 87ee5b8d
......@@ -24,6 +24,7 @@ set(ECM_MODULE_DIR ${CMAKE_SOURCE_DIR}/cmake)
include(ECMGeneratePriFile)
include(ECMEnableSanitizers)
include(ECMPoQmTools)
include(ECMQueryQmake)
include(FeatureSummary)
include(GenerateExportHeader)
......@@ -81,7 +82,7 @@ endif()
if(Qt5Core_FOUND)
set_package_properties(Qt5Core PROPERTIES TYPE REQUIRED)
find_package(Qt5 NO_MODULE REQUIRED COMPONENTS Network)
find_package(Qt5 NO_MODULE QUIET OPTIONAL_COMPONENTS Widgets Charts Test)
find_package(Qt5 NO_MODULE QUIET OPTIONAL_COMPONENTS Widgets Charts Test Help)
set_package_properties(Qt5 PROPERTIES URL "http://qt-project.org/")
set_package_properties(Qt5Widgets PROPERTIES TYPE RECOMMENDED PURPOSE "Required for feedback configuration and notification widgets.")
......@@ -159,6 +160,7 @@ add_subdirectory(autotests)
if(Qt5Core_FOUND)
add_subdirectory(tests)
endif()
add_subdirectory(docs)
#
# CMake package config file generation
......
#!/bin/bash
find "$@" -name '*.h' -o -name '*.cpp' -o -name '*.qml' -o -name '*.c' | grep -v /3rdparty/ | grep -v /build | while read FILE; do
find "$@" -name '*.h' -o -name '*.cpp' -o -name '*.qml' -o -name '*.c' -o -name "*.qdoc" | grep -v /3rdparty/ | grep -v /build | while read FILE; do
if grep -qiE "Copyright \(C\) [0-9, -]{4,} " "$FILE" ; then continue; fi
thisfile=`basename $FILE`
authorName=`git config user.name`
......
find_package(Qt5Core QUIET)
if (Qt5Core_FOUND)
set(_qmake_executable_default "qmake-qt5")
endif ()
if (TARGET Qt5::qmake)
get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
endif()
set(QMAKE_EXECUTABLE ${_qmake_executable_default}
CACHE FILEPATH "Location of the Qt5 qmake executable")
# This is not public API (yet)!
function(query_qmake result_variable qt_variable)
if(NOT QMAKE_EXECUTABLE)
set(${result_variable} "" PARENT_SCOPE)
message(WARNING "Should specify a qmake Qt5 binary. Can't check ${qt_variable}")
return()
endif()
execute_process(
COMMAND ${QMAKE_EXECUTABLE} -query "${qt_variable}"
RESULT_VARIABLE return_code
OUTPUT_VARIABLE output
)
if(return_code EQUAL 0)
string(STRIP "${output}" output)
file(TO_CMAKE_PATH "${output}" output_path)
set(${result_variable} "${output_path}" PARENT_SCOPE)
else()
message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query \"${qt_variable}\"")
message(FATAL_ERROR "QMake call failed: ${return_code}")
endif()
endfunction()
......@@ -22,6 +22,7 @@
#define USERFEEDBACK_VERSION_MINOR ${USERFEEDBACK_VERSION_MINOR}
#define USERFEEDBACK_VERSION_PATCH ${USERFEEDBACK_VERSION_PATCH}
#define USERFEEDBACK_VERSION "${USERFEEDBACK_VERSION}"
#define USERFEEDBACK_VERSION_MAJOR_MINOR "${USERFEEDBACK_VERSION_MAJOR}.${USERFEEDBACK_VERSION_MINOR}"
#define USERFEEDBACK_VERSION_STRING "${USERFEEDBACK_VERSION_STRING}"
#endif
find_program(QDOC_EXECUTABLE qdoc)
query_qmake(QT_INSTALL_DOCS "QT_INSTALL_DOCS")
query_qmake(QT_INSTALL_DATA "QT_INSTALL_DATA")
find_file(QDOC_TEMPLATE global/qt-html-templates-offline.qdocconf
HINTS ${QT_INSTALL_DOCS}
HINTS ${QT_INSTALL_DATA}
HINTS ${QT_INSTALL_DATA}/doc
)
get_filename_component(QDOC_TEMPLATE_DIR ${QDOC_TEMPLATE} DIRECTORY)
if(NOT QDOC_EXECUTABLE OR NOT TARGET Qt5::qhelpgenerator OR NOT TARGET Qt5::qcollectiongenerator OR NOT QDOC_TEMPLATE_DIR)
message(STATUS "Unable to build user manual:")
message(STATUS "qdoc exectuable: ${QDOC_EXECUTABLE}")
get_property(_path TARGET Qt5::qhelpgenerator PROPERTY LOCATION)
message(STATUS "qhelpgenerator exectuable: ${_path}")
get_property(_path TARGET Qt5::qcollectiongenerator PROPERTY LOCATION)
message(STATUS "qcollectiongenerator exectuable: ${_path}")
message(STATUS "qdoc template direectory: ${QDOC_TEMPLATE_DIR}")
return()
endif()
# build .qch
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/user-feedback-manual.qdocconf.in ${CMAKE_CURRENT_BINARY_DIR}/user-feedback-manual.qdocconf)
file(GLOB_RECURSE _qdoc_srcs ${CMAKE_CURRENT_SOURCE_DIR} "*.qdoc")
set(_qdoc_output_dir ${CMAKE_CURRENT_BINARY_DIR}/manual)
add_custom_command(
OUTPUT ${_qdoc_output_dir}/user-feedback-manual.qhp
COMMAND ${QDOC_EXECUTABLE}
--outputdir ${_qdoc_output_dir}
${CMAKE_CURRENT_BINARY_DIR}/user-feedback-manual.qdocconf
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/user-feedback-manual.qdocconf ${_qdoc_srcs}
)
add_custom_command(
OUTPUT ${_qdoc_output_dir}/user-feedback-manual.qch
COMMAND Qt5::qhelpgenerator ${_qdoc_output_dir}/user-feedback-manual.qhp
DEPENDS ${_qdoc_output_dir}/user-feedback-manual.qhp
)
add_custom_target(user_feedback_manual_qch ALL DEPENDS ${_qdoc_output_dir}/user-feedback-manual.qch)
install(FILES ${_qdoc_output_dir}/user-feedback-manual.qch DESTINATION share/KDE/UserFeedbackConsole)
# build .qhc
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/user-feedback.qhcp.in ${CMAKE_CURRENT_BINARY_DIR}/user-feedback.qhcp)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/user-feedback.qhc
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/manual/user-feedback-manual.qch ${CMAKE_CURRENT_BINARY_DIR}
COMMAND Qt5::qcollectiongenerator ${CMAKE_CURRENT_BINARY_DIR}/user-feedback.qhcp -o ${CMAKE_CURRENT_BINARY_DIR}/user-feedback.qhc
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/user-feedback.qhcp
user_feedback_manual_qch
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
add_custom_target(user_feedback_qhc ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/user-feedback.qhc)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/user-feedback.qhc DESTINATION share/KDE/UserFeedbackConsole)
<b>UserFeedbackConsole</b>
<p>UserFeedback management and analytics console.</p>
/*
Copyright (C) 2017 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
\page index.html
\nextpage Telemetry
\title UserFeedback Manual
\section1 Overview
This is a framework for collecting feedback from application users. This consists of two aspects:
\list
\li Telemetry
\li Surveys
\endlist
\section1 Table Of Contents
\list
\li \l{Telemetry}
\li \l{Surveys}
\endlist
*/
project = "UserFeedback"
description = "UserFeedback Manual"
url = https://cgit.kde.org/kuserfeedback.git/
sourcedirs = @CMAKE_CURRENT_SOURCE_DIR@
sources.fileextensions = "*.qdoc"
qhp.projects = UserFeedback
qhp.UserFeedback.file = user-feedback-manual.qhp
qhp.UserFeedback.namespace = org.kde.UserFeedback.@USERFEEDBACK_VERSION_MAJOR@.@USERFEEDBACK_VERSION_MINOR@
qhp.UserFeedback.virtualFolder = userfeedback
qhp.UserFeedback.indexTitle = UserFeedback Manual
qhp.UserFeedback.filterAttributes = userfeedback @USERFEEDBACK_VERSION@
qhp.UserFeedback.customFilters.UserFeedback.name = UserFeedback @USERFEEDBACK_VERSION@
qhp.UserFeedback.customFilters.UserFeedback.filterAttributes = userfeedback @USERFEEDBACK_VERSION@
qhp.UserFeedback.indexRoot =
qhp.UserFeedback.subprojects = manual
qhp.UserFeedback.subprojects.manual.title = UserFeedback Manual
qhp.UserFeedback.subprojects.manual.indexTitle = UserFeedback Manual
qhp.UserFeedback.subprojects.manual.type = manual
navigation.homepage = "UserFeedback Manual"
navigation.landingpage = "UserFeedback Manual"
buildversion = "UserFeedback Manual @USERFEEDBACK_VERSION_STRING@"
# offline specific
include(@QDOC_TEMPLATE_DIR@/qt-html-templates-offline.qdocconf)
# adapted from qtbase template, to remove the TQC copyright notice
HTML.footer = \
" </div>\n" \
" </div>\n" \
" </div>\n" \
" </div>\n" \
"</div>\n" \
"<div class=\"footer\">\n" \
" <p>\n" \
" <acronym title=\"Copyright\">&copy;</acronym> 2017 Volker Krause.\n" \
" This work is licensed under LGPLv2+." \
"</p>" \
"</div>\n" \
/*
Copyright (C) 2017 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
\page user-feedback-surveys.html
\previouspage Telemetry
\nextpage UserFeedback Manual
\title Surveys
\section1 Overview
Surveys have the following properties:
\list
\li A name. This is purely used for making survey management easier, it's not shown
to the user.
\li A URL. This link is opened if a user chooses to participate in a survey.
\li An active flag. Only active surveys are presented to users.
\li A targeting expression. Only surveys where this expression evaluates to \c true
for a specific user are presented. The targeting expression is optional, if it is
not set, \c true is implied, ie. the survey is considered for all users.
\endlist
\section1 Targeting Expressions
Survey targeting expressions are boolean expressions with a C++-like syntax.
\section2 Data Types
Values and literals of the following types are supported:
\list
\li Integers.
\li Floats.
\li Boolean. Literals are specified using the keywords \c true and \c false.
\li Strings. Literals need to be put in double quotes, special characters in literals
need to be escaped with backslashes as in C++.
\endlist
\section2 Values
The value of a scalar data source is accessed my using the source name and the element name
separated by a dot, as follows:
\code
platform.os
\endcode
Accessing the value of a list or map data source is done by adding the corresponding array
index or map key in brackets:
\code
screens[0].width
\endcode
Additionally, the amount of entries in a list or map data source is available via \c .size:
\code
screens.size
\endcode
\section2 Comparisons
The following comparison operators are supported, comparisons can be done in any combination
between values and literals.
\table
\header
\li Operator
\li Semantic
\row
\li ==
\li Equality
\row
\li !=
\li Inequality
\row
\li >
\li Greater than (not supported for boolean values)
\row
\li >=
\li Greator or equal (not supported for boolean values)
\row
\li <
\li Less than (not supported for boolean values)
\row
\li <=
\li Less or equal (not supported for boolean values)
\endtable
\section2 Boolean Expressions
Comparisons can be combined by logical AND (\c &&) and OR (\c ||) operators. Further, expression
evaluation order can be controlled by parenthesis.
The following examples targets users with multi-screen setups on windows:
\code
screens.size >= 2 && platform.os == "windows"
\endcode
*/
/*
Copyright (C) 2017 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
\page user-feedback-telemetry.html
\previouspage UserFeedback Manual
\nextpage Surveys
\title Telemetry
\section1 Overview
Telemetry for a product consists of a set of data sources. Each data source
can be a scalar, list or map, and consists of one or more elements.
\section1 Data Model
A data source represents a logically grouped and indivisible set of telemetry
data of the application.
If you are familiar with JSON, it might be useful to see the data model as
JSON structures with a depth limit of two.
\section2 Source Type
A data source has one of the following types:
\list
\li Scalar. A scalar source transmits a single sample of its elements.
A typical example would be a OS type/version source.
\li List. A list source transmits a sequence of its elements. The size
of that sequence is not pre-defined and can be empty. A typical example
would be the users screen configuration.
\li Map. A map source transmits a set of its elements keyed by a string
identifer. Map keys are not pre-defined but can contain arbitrary values.
A typical example would be the usage ratios of different views of the application.
\endlist
The type of a data source is static and defined in the product schema.
\section2 Elements
Each data source has one or more elements. Elements are named fields that contain
values of basic data types, and are fixed and pre-defined in the product schema.
The following data types are supported for source elements:
\list
\li Boolean
\li Integer
\li Float
\li String
\endlist
\section1 Aggregations
Aggregations of telemetry data have no impact on users, they purely serve analytics.
Aggregations use one or more data source elements as input. The following aggregations
are currently implemented:
\list
\li Category. The category aggregator counts the amount of samples grouped by the
value of the selected data source element. It can be used on any element which contains
a reasonably small set of discrete values. A typical example would be the OS type.
\li Numeric. The numeric aggregator computes the minimum/maximum and median values of
the selected data source element. It can be used on any element which contains numeric
values. A typical example would be the start count or usage time of the application.
\li RatioSet. The ratio set aggregator shows the distribution of a map data source
containing normalized numeric data. This is typically used for the usage ratio of
different views or features in the application.
\endlist
*/
<?xml version="1.0" encoding="utf-8" ?>
<QHelpCollectionProject version="1.0">
<assistant>
<title>UserFeedback Help</title>
<!--<applicationIcon>TODO</applicationIcon>-->
<cacheDirectory>KDE/UserFeedback</cacheDirectory>
<homePage>qthelp://org.kde.UserFeedback.@USERFEEDBACK_VERSION_MAJOR@.@USERFEEDBACK_VERSION_MINOR@/userfeedback/index.html</homePage>
<startPage>qthelp://org.kde.UserFeedback.@USERFEEDBACK_VERSION_MAJOR@.@USERFEEDBACK_VERSION_MINOR@/userfeedback/index.html</startPage>
<aboutMenuText>
<text>About UserFeedbackConsole...</text>
<!-- TODO: we could add translated texts here... -->
</aboutMenuText>
<aboutDialog>
<!-- TODO fill from .ts files -->
<file>@CMAKE_CURRENT_SOURCE_DIR@/about.txt</file>
<!--<icon>TODO</icon>-->
</aboutDialog>
<enableDocumentationManager>false</enableDocumentationManager>
<enableAddressBar>false</enableAddressBar>
<enableFilterFunctionality>false</enableFilterFunctionality>
</assistant>
<docFiles>
<register>
<file>user-feedback-manual.qch</file>
</register>
</docFiles>
</QHelpCollectionProject>
......@@ -46,6 +46,7 @@ set(console_srcs
connectdialog.cpp
main.cpp
mainwindow.cpp
helpcontroller.cpp
analytics/aggregator.cpp
analytics/analyticsview.cpp
......
/*
Copyright (C) 2017 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config-userfeedback-version.h>
#include "helpcontroller.h"
#include <QByteArray>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QLibraryInfo>
#include <QProcess>
#include <QStandardPaths>
namespace UserFeedback {
namespace Console {
struct HelpControllerPrivate
{
HelpControllerPrivate()
: proc(nullptr) {}
void startProcess();
void sendCommand(const QByteArray &cmd);
QString assistantPath;
QString qhcPath;
QProcess *proc;
};
}}
using namespace UserFeedback::Console;
void HelpControllerPrivate::startProcess()
{
if (proc)
return;
proc = new QProcess(QCoreApplication::instance());
proc->setProcessChannelMode(QProcess::ForwardedChannels);
QObject::connect(proc, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), [this]() {
proc->deleteLater();
proc = nullptr;
});
proc->setProgram(assistantPath);
proc->setArguments({ QLatin1String("-collectionFile"), qhcPath, QLatin1String("-enableRemoteControl") });
proc->start();
proc->waitForStarted();
sendCommand("expandToc 2;");
}
void HelpControllerPrivate::sendCommand(const QByteArray &cmd)
{
if (!proc)
return;
proc->write(cmd);
}
Q_GLOBAL_STATIC(HelpControllerPrivate, s_helpController)
bool HelpController::isAvailable()
{
auto d = s_helpController();
if (!d->assistantPath.isEmpty() && !d->qhcPath.isEmpty())
return true;
d->assistantPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator() + QStringLiteral("assistant");
QFileInfo assistFile(d->assistantPath);
if (!assistFile.isExecutable()) {
d->assistantPath = QStandardPaths::findExecutable(QStringLiteral("assistant"));
if (d->assistantPath.isEmpty()) {
qDebug() << "Qt Assistant not found, help not available.";
return false;
}
}
const QString qhcPath = QStandardPaths::locate(QStandardPaths::DataLocation, QLatin1String("user-feedback.qhc"));
if (QFileInfo::exists(qhcPath)) {
d->qhcPath = qhcPath;
return true;
}
qDebug() << "user-feedback.qhc not found - help not available";
return false;
}
void HelpController::openContents()
{
Q_ASSERT(isAvailable());
auto d = s_helpController();
d->startProcess();
d->sendCommand("setSource qthelp://org.kde.UserFeedback." USERFEEDBACK_VERSION_MAJOR_MINOR "/userfeedback/index.html;syncContents\n");
}
void HelpController::openPage(const QString &page)
{
Q_ASSERT(isAvailable());
auto d = s_helpController();
d->startProcess();
d->sendCommand(QByteArray("setSource qthelp://org.kde.UserFeedback." USERFEEDBACK_VERSION_MAJOR_MINOR "/") + page.toUtf8() + ";syncContents\n");
}
/*
Copyright (C) 2017 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef USERFEEDBACK_CONSOLE_HELPCONTROLLER_H
#define USERFEEDBACK_CONSOLE_HELPCONTROLLER_H
class QString;
namespace UserFeedback {
namespace Console {
/*! Controls the Assistant-based help browser. */
namespace HelpController
{
/*! Returns @c true if Assistant and our help collection are found. */
bool isAvailable();
/*! Open start page of the help collection. */
void openContents();
/*! Opens the specified page of the help collection. */
void openPage(const QString &page);
}
}}
#endif // USERFEEDBACK_CONSOLE_HELPCONTROLLER_H
......@@ -18,7 +18,7 @@
#include <config-userfeedback-version.h>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "helpcontroller.h"
#include "connectdialog.h"
#include <model/productmodel.h>
......@@ -123,6 +123,12 @@ MainWindow::MainWindow() :
connect(ui->actionQuit, &QAction::triggered, this, &QMainWindow::close);
ui->menuWindow->addAction(ui->productsDock->toggleViewAction());
ui->menuWindow->addAction(ui->logDock->toggleViewAction());
ui->actionUserManual->setEnabled(HelpController::isAvailable());
ui->actionUserManual->setShortcut(QKeySequence::HelpContents);