From 323f60b25f0872ddee04f52aec374b741dbf9d17 Mon Sep 17 00:00:00 2001 From: Aaron Seigo Date: Sun, 7 Dec 2014 16:06:23 +0100 Subject: How Are We Doing (hawd) framework --- tests/hawd/CMakeLists.txt | 16 ++++ tests/hawd/datasetdefinition.cpp | 195 +++++++++++++++++++++++++++++++++++++++ tests/hawd/datasetdefinition.h | 83 +++++++++++++++++ tests/hawd/main.cpp | 56 +++++++++++ tests/hawd/module.cpp | 170 ++++++++++++++++++++++++++++++++++ tests/hawd/module.h | 66 +++++++++++++ tests/hawd/modules/list.cpp | 79 ++++++++++++++++ tests/hawd/modules/list.h | 37 ++++++++ tests/hawd/state.cpp | 99 ++++++++++++++++++++ tests/hawd/state.h | 47 ++++++++++ 10 files changed, 848 insertions(+) create mode 100644 tests/hawd/CMakeLists.txt create mode 100644 tests/hawd/datasetdefinition.cpp create mode 100644 tests/hawd/datasetdefinition.h create mode 100644 tests/hawd/main.cpp create mode 100644 tests/hawd/module.cpp create mode 100644 tests/hawd/module.h create mode 100644 tests/hawd/modules/list.cpp create mode 100644 tests/hawd/modules/list.h create mode 100644 tests/hawd/state.cpp create mode 100644 tests/hawd/state.h (limited to 'tests/hawd') diff --git a/tests/hawd/CMakeLists.txt b/tests/hawd/CMakeLists.txt new file mode 100644 index 0000000..8c677c0 --- /dev/null +++ b/tests/hawd/CMakeLists.txt @@ -0,0 +1,16 @@ +project(hawd) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +set(SRCS + datasetdefinition.cpp + main.cpp + module.cpp + state.cpp + modules/list.cpp +) + +add_executable(${PROJECT_NAME} ${SRCS}) +qt5_use_modules(${PROJECT_NAME} Core) +install(TARGETS ${PROJECT_NAME} DESTINATION bin) + diff --git a/tests/hawd/datasetdefinition.cpp b/tests/hawd/datasetdefinition.cpp new file mode 100644 index 0000000..550c4fb --- /dev/null +++ b/tests/hawd/datasetdefinition.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "datasetdefinition.h" + +#include +#include +#include +#include + +#include + +namespace HAWD +{ + +static QHash s_types; + +DataDefinition::DataDefinition(const QString &name, QMetaType::Type type, const QString &unit, int min, int max) + : m_name(name), + m_type(type), + m_unit(unit), + m_min(min), + m_max(max) +{ +} + +DataDefinition::DataDefinition(const QJsonObject &json) + : m_name(json.value("name").toString()), + m_type(QMetaType::Int), + m_unit(json.value("unit").toString()), + m_min(json.value("min").toInt()), + m_max(json.value("max").toInt()) +{ + if (s_types.isEmpty()) { + s_types.insert("date", QMetaType::QDate); + s_types.insert("time", QMetaType::QTime); + s_types.insert("int", QMetaType::Int); + s_types.insert("uint", QMetaType::UInt); + s_types.insert("bool", QMetaType::Bool); + s_types.insert("char", QMetaType::QChar); + s_types.insert("string", QMetaType::QString); + s_types.insert("datetime", QMetaType::QDateTime); + } + + const QString typeString = json.value("type").toString().toLower(); + if (s_types.contains(typeString)) { + m_type = s_types.value(typeString); + } +} + +QString DataDefinition::name() const +{ + return m_name; +} + +QString DataDefinition::typeString() const +{ + return QMetaType::typeName(m_type); +} + +QMetaType::Type DataDefinition::type() const +{ + return m_type; +} + +QString DataDefinition::unit() const +{ + return m_unit; +} + +int DataDefinition::min() const +{ + return m_min; +} + +int DataDefinition::max() const +{ + return m_max; +} + +DatasetRow::DatasetRow(const QHash &columns) + : m_columns(columns) +{ + QHashIterator it(columns); + while (it.hasNext()) { + it.next(); + m_data.insert(it.key(), QVariant()); + } +} + +void DatasetRow::setValue(const QString &column, const QVariant &value) +{ + if (!m_columns.contains(column) || !value.canConvert(m_columns[column].type())) { + return; + } + + m_data[column] = value; +} + +void DatasetRow::annotate(const QString ¬e) +{ + m_annotation = note; +} + +QString DatasetRow::toString() const +{ + QString string; + QHashIterator it(m_data); + while (it.hasNext()) { + it.next(); + if (!string.isEmpty()) { + string.append('\t'); + } + + string.append(it.value().toString()); + } + + if (!m_annotation.isEmpty()) { + string.append('\t').append(m_annotation); + } + + return string; +} + +DatasetDefinition::DatasetDefinition(const QString &path) + : m_valid(false) +{ + QFile file(path); + m_name = file.fileName(); + + if (file.open(QIODevice::ReadOnly)) { + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll(), &error); + + if (jsonDoc.isNull()) { + std::cerr << QObject::tr("Dataset definition file malformed at character %1: %2").arg(error.offset).arg(error.errorString()).toStdString() << std::endl; + } else { + m_valid = true; + QJsonObject json = jsonDoc.object(); + const QString name = json.value("name").toString(); + if (!name.isEmpty()) { + m_name = name; + } + + m_description = json.value("description").toString(); + QJsonObject cols = json.value("columns").toObject(); + for (const QString &key: cols.keys()) { + QJsonObject def = cols.value(key).toObject(); + if (!def.isEmpty()) { + m_columns.insert(key, DataDefinition(def)); + } + } + } + } +} + + +bool DatasetDefinition::isValid() const +{ + return m_valid; +} + +QString DatasetDefinition::name() const +{ + return m_name; +} + +QString DatasetDefinition::description() const +{ + return m_description; +} + +QHash DatasetDefinition::columns() const +{ + return m_columns; +} + +} // namespace HAWD + diff --git a/tests/hawd/datasetdefinition.h b/tests/hawd/datasetdefinition.h new file mode 100644 index 0000000..0593f39 --- /dev/null +++ b/tests/hawd/datasetdefinition.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include +#include +#include + +namespace HAWD +{ + +class DataDefinition +{ +public: + DataDefinition(const QString &name = QString(), QMetaType::Type type = QMetaType::Void, const QString &unit = QString(), int min = 0, int max = 0); + DataDefinition(const QJsonObject &object); + + QString name() const; + QMetaType::Type type() const; + QString typeString() const; + QString unit() const; + int min() const; + int max() const; + +private: + QString m_name; + QMetaType::Type m_type; + QString m_unit; + int m_min; + int m_max; +}; + +class DatasetRow +{ +public: + DatasetRow(const QHash &columns); + void setValue(const QString &column, const QVariant &value); + void annotate(const QString ¬e); + QString toString() const; + +private: + QHash m_columns; + QHash m_data; + QString m_annotation; +}; + +class DatasetDefinition +{ +public: + DatasetDefinition(const QString &path); + + bool isValid() const; + + QString name() const; + QString description() const; + QHash columns() const; + +private: + bool m_valid; + QString m_name; + QString m_description; + QHash m_columns; +}; + +} // namespace HAWD + diff --git a/tests/hawd/main.cpp b/tests/hawd/main.cpp new file mode 100644 index 0000000..fb07b08 --- /dev/null +++ b/tests/hawd/main.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include "module.h" + +#include +#include + +#include + +void printHelp() +{ + std::cout << QCoreApplication::translate("main", "Usage of the How Are We Doing (hawd) command line tool:").toStdString() << std::endl; + HAWD::Module::printCommands(); +} + +int main(int argc, char *argv[]) +{ + // load all modules + HAWD::Module::loadModules(); + HAWD::State state; + + if (!state.isValid()) { + exit(1); + } + + QCoreApplication app(argc, argv); + app.setApplicationName("hawd"); + + QStringList commands = app.arguments(); + commands.removeFirst(); + + if (commands.isEmpty()) { + printHelp(); + } + + return HAWD::Module::match(commands, state); +} diff --git a/tests/hawd/module.cpp b/tests/hawd/module.cpp new file mode 100644 index 0000000..c020e06 --- /dev/null +++ b/tests/hawd/module.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "module.h" + +#include "modules/list.h" + +#include + +#include + +namespace HAWD +{ + +QVector Module::s_modules; + +Module::Syntax::Syntax() +{ +} + +Module::Syntax::Syntax(const QString &k, std::function l, bool e) + : keyword(k), + lambda(l), + eventDriven(e) +{ +} + +Module::Module() +{ +} + +void Module::loadModules() +{ + addModule(List()); +} + +void Module::printCommands() +{ + for (const Module &module: s_modules) { + printSyntax(1, module.syntax(), module.description()); + } +} + +void Module::printSyntax(uint indent, const Syntax &syntax, const QString &description) +{ + const std::string indentation(indent, '\t'); + std::cout << indentation; + + if (indent < 2) { + std::cout << "hawd "; + } + + std::cout << syntax.keyword.toStdString(); + + if (!description.isEmpty()) { + std::cout << ": " << description.toStdString(); + } + + std::cout << std::endl; + + for (const Syntax &child: syntax.children) { + printSyntax(indent + 1, child); + } +} + +void Module::addModule(const Module &module) +{ + s_modules.append(module); +} + +QVector Module::modules() +{ + return s_modules; +} + +bool Module::match(const QStringList &commands, State &state) +{ + for (const Module &module: s_modules) { + if (module.matches(commands, state)) { + return true; + } + } + + return false; +} + +Module::Syntax Module::syntax() const +{ + return m_syntax; +} + +void Module::setSyntax(const Syntax &syntax) +{ + m_syntax = syntax; +} + +QString Module::description() const +{ + return m_description; +} + +void Module::setDescription(const QString &description) +{ + m_description = description; +} + +bool Module::matches(const QStringList &commands, State &state) const +{ + if (commands.isEmpty()) { + return false; + } + + QStringListIterator commandIt(commands); + + if (commandIt.next() != m_syntax.keyword) { + return false; + } + + QListIterator syntaxIt(m_syntax.children); + const Syntax *syntax = &m_syntax; + QStringList tailCommands; + while (commandIt.hasNext() && syntaxIt.hasNext()) { + const QString word = commandIt.next(); + while (syntaxIt.hasNext()) { + const Syntax &child = syntaxIt.next(); + if (word == child.keyword) { + syntax = &child; + syntaxIt = child.children; + } + } + + if (!syntaxIt.hasNext()) { + tailCommands << word; + break; + } + } + + if (syntax && syntax->lambda) { + while (commandIt.hasNext()) { + tailCommands << commandIt.next(); + } + + bool rv = syntax->lambda(tailCommands, state); + if (rv && syntax->eventDriven) { + return QCoreApplication::instance()->exec(); + } + + return rv; + } + + return false; +} + +} // namespace HAWD diff --git a/tests/hawd/module.h b/tests/hawd/module.h new file mode 100644 index 0000000..8f94707 --- /dev/null +++ b/tests/hawd/module.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include "state.h" + +#include +#include + +namespace HAWD +{ + +class Module +{ +public: + struct Syntax + { + Syntax(); + Syntax(const QString &keyword, std::function lambda = std::function(), bool eventDriven = false); + QString keyword; + std::function lambda; + QList children; + bool eventDriven; + }; + + static void addModule(const Module &module); + static QVector modules(); + static bool match(const QStringList &commands, State &state); + static void loadModules(); + static void printCommands(); + + Module(); + + Module::Syntax syntax() const; + void setSyntax(const Syntax &syntax); + + QString description() const; + void setDescription(const QString &description); + + bool matches(const QStringList &commands, State &state) const; + +private: + static void printSyntax(uint indent, const Syntax &syntax, const QString &description = QString()); + Syntax m_syntax; + QString m_description; + static QVector s_modules; +}; + +} // namespace HAWD diff --git a/tests/hawd/modules/list.cpp b/tests/hawd/modules/list.cpp new file mode 100644 index 0000000..3310217 --- /dev/null +++ b/tests/hawd/modules/list.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "list.h" + +#include "../datasetdefinition.h" + +#include +#include +#include + +#include + +namespace HAWD +{ + +List::List() + : Module() +{ + Syntax top("list", &List::list); + //top.children << Syntax("create", &List::create); + setSyntax(top); +} + +bool List::list(const QStringList &commands, State &state) +{ + QDir project(state.projectPath()); + + if (commands.isEmpty()) { + project.setFilter(QDir::Files | QDir::Readable | QDir::NoDotAndDotDot | QDir::NoSymLinks); + const QStringList entryList = project.entryList(); + + if (entryList.isEmpty()) { + std::cout << QObject::tr("No data sets in this project").toStdString() << std::endl; + } else { + std::cout << QObject::tr("Data sets in this project:").toStdString() << std::endl; + for (const QString &file: project.entryList()) { + std::cout << '\t' << file.toStdString() << std::endl; + } + } + } else { + for (const QString &file: commands) { + DatasetDefinition dataset(project.absoluteFilePath(file)); + if (dataset.isValid()) { + DatasetDefinition dataset(project.absoluteFilePath(file)); + std::cout << '\t' << QObject::tr("Dataset: %1").arg(dataset.name()).toStdString() << std::endl; + QHashIterator it(dataset.columns()); + while (it.hasNext()) { + it.next(); + std::cout << "\t\t" << it.value().typeString().toStdString() << ' ' << it.key().toStdString() << std::endl; + } + + } else { + std::cout << QObject::tr("Invalid or non-existent dataset definition at %1").arg(project.absoluteFilePath(file)).toStdString() << std::endl; + } + } + } + + return true; +} + +} // namespace HAWD + diff --git a/tests/hawd/modules/list.h b/tests/hawd/modules/list.h new file mode 100644 index 0000000..3bd81e2 --- /dev/null +++ b/tests/hawd/modules/list.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include "module.h" + +namespace HAWD +{ + +class List : public Module +{ +public: + List(); + +private: + static bool list(const QStringList &commands, State &state); +}; + +} // namespace HAWD + diff --git a/tests/hawd/state.cpp b/tests/hawd/state.cpp new file mode 100644 index 0000000..cfc4326 --- /dev/null +++ b/tests/hawd/state.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "state.h" + +#include +#include +#include +#include + +#include + +static const QString configFileName("hawd.conf"); + +namespace HAWD +{ + +State::State() + : m_valid(true) +{ + QDir dir; + QString configPath; + + while (!dir.exists(configFileName) && dir.cdUp()) { } + + if (dir.exists(configFileName)) { + configPath = dir.absoluteFilePath(configFileName); + } + + if (configPath.isEmpty()) { + std::cerr << QObject::tr("Could not find hawd configuration. A hawd.conf file must be in the current directory or in a directory above it.").toStdString() << std::endl; + m_valid = false; + return; + } + + QFile configFile(configPath); + if (configFile.open(QIODevice::ReadOnly)) { + QJsonParseError error; + QJsonDocument config = QJsonDocument::fromJson(configFile.readAll(), &error); + if (config.isNull()) { + std::cerr << QObject::tr("Error parsing config file at %1").arg(configPath).toStdString() << std::endl; + std::cerr << '\t' << error.errorString().toStdString(); + } else { + m_configData = config.object(); + } + } +} + +bool State::isValid() const +{ + return m_valid; +} + +QString tildeExpand(QString path) +{ + if (path.isEmpty() || path.at(0) != '~') { + return path; + } + + return path.replace('~', QDir::homePath()); +} + +QString State::resultsPath() const +{ + return tildeExpand(configValue("results").toString()); +} + +QString State::projectPath() const +{ + return tildeExpand(configValue("project").toString()); +} + +DatasetDefinition State::datasetDefinition(const QString &name) const +{ + return DatasetDefinition(projectPath() + '/' + name); +} + +QVariant State::configValue(const QString &key) const +{ + return m_configData.value(key).toVariant(); +} + +} // namespace HAWD diff --git a/tests/hawd/state.h b/tests/hawd/state.h new file mode 100644 index 0000000..5e4d3c2 --- /dev/null +++ b/tests/hawd/state.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include +#include + +#include "datasetdefinition.h" + +namespace HAWD +{ + +class State +{ +public: + State(); + + bool isValid() const; + QVariant configValue(const QString &key) const; + QString resultsPath() const; + QString projectPath() const; + DatasetDefinition datasetDefinition(const QString &name) const; + +private: + bool m_valid; + QJsonObject m_configData; +}; + +} // namespace HAWD + -- cgit v1.2.3