Skip to content

Commit

Permalink
Allow the type of log to be specified in AddTimeSeriesLog
Browse files Browse the repository at this point in the history
Refs #9227
  • Loading branch information
martyngigg committed Apr 1, 2014
1 parent c2f11f9 commit f3588ba
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 25 deletions.
67 changes: 54 additions & 13 deletions Code/Mantid/Framework/Algorithms/src/AddTimeSeriesLog.cpp
@@ -1,7 +1,11 @@
/*WIKI*
Creates/updates a time-series log entry on a chosen workspace. The given timestamp & value are appended to the
named log entry. If the named entry does not exist then a new log is created. A time stamp must be given in
ISO8601 format, e.g. 2010-09-14T04:20:12."
ISO8601 format, e.g. 2010-09-14T04:20:12.
By default, the given value is interpreted as a double and a double series is either created or expected. However,
if the "Type" is set to "int" then the value is interpreted as an integer and an integer is either created
or expected.
*WIKI*/
/*WIKI_USAGE*
'''Python'''
Expand All @@ -16,8 +20,9 @@ ISO8601 format, e.g. 2010-09-14T04:20:12."

#include "MantidAlgorithms/AddTimeSeriesLog.h"
#include "MantidKernel/DateTimeValidator.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/MandatoryValidator.h"
#include "MantidKernel/ListValidator.h"
#include "MantidKernel/TimeSeriesProperty.h"

namespace Mantid
{
Expand All @@ -26,6 +31,38 @@ namespace Mantid
using namespace API;
using namespace Kernel;

namespace
{
/**
* Creates/updates the named log on the given run. The template type
* specifies either the type of TimeSeriesProperty that is expected to
* exist or the type that will be created
* @param run A reference to the run object that stores the logs
* @param name The name of the log that is to be either created or updated
* @param time A time string in ISO format, passed to the DateAndTime constructor
* @param value The value at the given time
*/
template<typename T>
void createOrUpdate(API::Run& run, const std::string & name,
const std::string & time, const T value)
{
TimeSeriesProperty<T> *timeSeries(NULL);
if(run.hasProperty(name))
{
timeSeries = dynamic_cast<TimeSeriesProperty<T>*>(run.getLogData(name));
if(!timeSeries) throw std::invalid_argument("Log '" + name + "' already exists but the values are a different type.");
}
else
{
timeSeries = new TimeSeriesProperty<T>(name);
run.addProperty(timeSeries);
}
timeSeries->addValue(time, value);
}

}


// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(AddTimeSeriesLog)

Expand Down Expand Up @@ -66,6 +103,14 @@ namespace Mantid
auto nonEmtpyDbl = boost::make_shared<MandatoryValidator<double>>();
declareProperty("Value", EMPTY_DBL(), nonEmtpyDbl, "The value for the log at the given time",
Direction::Input);

auto optionsValidator = boost::make_shared<ListValidator<std::string>>();
optionsValidator->addAllowedValue("double");
optionsValidator->addAllowedValue("int");
declareProperty("Type", "double", optionsValidator,
"An optional type for the given value. A double value with a Type=int will have "
"the fractional part chopped off.",
Direction::Input);
}

//----------------------------------------------------------------------------------------------
Expand All @@ -75,26 +120,22 @@ namespace Mantid
void AddTimeSeriesLog::exec()
{
MatrixWorkspace_sptr logWS = getProperty("Workspace");
auto &run = logWS->mutableRun();
std::string name = getProperty("Name");
std::string time = getProperty("Time");
double value = getProperty("Value");
double valueAsDouble = getProperty("Value");
std::string type = getProperty("Type");
bool asInt = (type == "int");

auto & run = logWS->mutableRun();
TimeSeriesProperty<double> *timeSeries(NULL);
if(run.hasProperty(name))
if(asInt)
{
timeSeries = dynamic_cast<TimeSeriesProperty<double>*>(run.getLogData(name));
if(!timeSeries) throw std::invalid_argument("Log '" + name + "' already exists but is not a time series.");
createOrUpdate<int>(run, name, time, static_cast<int>(valueAsDouble));
}
else
{
timeSeries = new TimeSeriesProperty<double>(name);
run.addProperty(timeSeries);
createOrUpdate<double>(run, name, time, valueAsDouble);
}
timeSeries->addValue(time, value);
}



} // namespace Algorithms
} // namespace Mantid
55 changes: 43 additions & 12 deletions Code/Mantid/Framework/Algorithms/test/AddTimeSeriesLogTest.h
Expand Up @@ -8,31 +8,49 @@

class AddTimeSeriesLogTest : public CxxTest::TestSuite
{
private:
enum LogType { Double, Integer };

public:
// This pair of boilerplate methods prevent the suite being created statically
// This means the constructor isn't called when running other tests
static AddTimeSeriesLogTest *createSuite() { return new AddTimeSeriesLogTest(); }
static void destroySuite( AddTimeSeriesLogTest *suite ) { delete suite; }

void test_Workspace2D()
void test_defaults_create_a_double_type_series()
{
auto ws = WorkspaceCreationHelper::Create2DWorkspace(10,10);
TS_ASSERT_THROWS_NOTHING(executeAlgorithm(ws, "Test Name", "2010-09-14T04:20:12", 20.0));
checkLogWithEntryExists(ws, "Test Name", "2010-09-14T04:20:12", 20.0, 0);
checkLogWithEntryExists<double>(ws, "Test Name", "2010-09-14T04:20:12", 20.0, 0);
TS_ASSERT_THROWS_NOTHING(executeAlgorithm(ws, "Test Name", "2010-09-14T04:20:19", 40.0));
checkLogWithEntryExists(ws, "Test Name", "2010-09-14T04:20:19", 40.0, 1);
checkLogWithEntryExists<double>(ws, "Test Name", "2010-09-14T04:20:19", 40.0, 1);
}

void test_forcing_to_int_creates_int_from_double()
{
auto ws = WorkspaceCreationHelper::Create2DWorkspace(10,10);
TS_ASSERT_THROWS_NOTHING(executeAlgorithm(ws, "Test Name", "2010-09-14T04:20:12", 20.5, Integer));

checkLogWithEntryExists<int>(ws, "Test Name", "2010-09-14T04:20:12", 20, 0);
TS_ASSERT_THROWS_NOTHING(executeAlgorithm(ws, "Test Name", "2010-09-14T04:20:19", 40.0, Integer));
checkLogWithEntryExists<int>(ws, "Test Name", "2010-09-14T04:20:19", 40, 1);

}

void test_EventWorkspace()
void test_algorithm_only_accepts_int_or_double_as_Type()
{
auto ws = WorkspaceCreationHelper::CreateEventWorkspace(10,10);
TS_ASSERT_THROWS_NOTHING(executeAlgorithm(ws, "Test Name", "2010-09-14T04:20:12", 20.0));
checkLogWithEntryExists(ws, "Test Name", "2010-09-14T04:20:12", 20.0, 0);
TS_ASSERT_THROWS_NOTHING(executeAlgorithm(ws, "Test Name", "2010-09-14T04:20:19", 40.0));
checkLogWithEntryExists(ws, "Test Name", "2010-09-14T04:20:19", 40.0, 1);
Mantid::Algorithms::AddTimeSeriesLog alg;
TS_ASSERT_THROWS_NOTHING(alg.initialize());

const Mantid::Kernel::Property *prop = alg.getProperty("Type");
const auto allowedValues = prop->allowedValues();

TS_ASSERT_EQUALS(2, allowedValues.size());
TS_ASSERT_EQUALS(1, allowedValues.count("int"));
TS_ASSERT_EQUALS(1, allowedValues.count("double"));
}


//-------------------------- Failure cases ------------------------------------
void test_empty_log_name_not_allowed()
{
Expand Down Expand Up @@ -76,10 +94,21 @@ class AddTimeSeriesLogTest : public CxxTest::TestSuite
TS_ASSERT_THROWS(executeAlgorithm(ws, "Test Name", "2010-09-14T04:20:12", 20.0), std::invalid_argument);
}

void test_algorithm_fails_if_time_series_exists_but_it_is_incorrect_type()
{
auto ws = WorkspaceCreationHelper::Create2DWorkspace(10,10);
auto & run = ws->mutableRun();
const std::string logName = "DoubleSeries";
auto *timeSeries = new Mantid::Kernel::TimeSeriesProperty<double>(logName);
timeSeries->addValue("2010-09-14T04:20:12", 20.0);
run.addLogData(timeSeries);
TS_ASSERT_THROWS(executeAlgorithm(ws, logName, "2010-09-14T04:20:30", 30, Integer), std::invalid_argument);
}

private:

void executeAlgorithm(Mantid::API::MatrixWorkspace_sptr testWS, const std::string & logName, const std::string & logTime,
const double logValue)
const double logValue, const LogType type = Double)
{
//execute algorithm
Mantid::Algorithms::AddTimeSeriesLog alg;
Expand All @@ -91,12 +120,14 @@ class AddTimeSeriesLogTest : public CxxTest::TestSuite
alg.setPropertyValue("Name", logName);
alg.setPropertyValue("Time", logTime);
alg.setProperty("Value", logValue);
if(type == Integer) alg.setProperty("Type", "int");
alg.setRethrows(true);
alg.execute();
}

template<typename T>
void checkLogWithEntryExists(Mantid::API::MatrixWorkspace_sptr testWS, const std::string & logName, const std::string & logTime,
const double logValue, const size_t position)
const T logValue, const size_t position)
{
using Mantid::Kernel::DateAndTime;
using Mantid::Kernel::TimeSeriesProperty;
Expand All @@ -105,7 +136,7 @@ class AddTimeSeriesLogTest : public CxxTest::TestSuite
TSM_ASSERT("Run does not contain the expected log entry", run.hasProperty(logName));

auto *prop = run.getLogData(logName);
auto *timeSeries = dynamic_cast<TimeSeriesProperty<double>*>(prop);
auto *timeSeries = dynamic_cast<TimeSeriesProperty<T>*>(prop);
TSM_ASSERT("A log entry with the given name exists but it is not a time series", timeSeries);
auto times = timeSeries->timesAsVector();
TS_ASSERT(times.size() >= position + 1);
Expand Down

0 comments on commit f3588ba

Please sign in to comment.