Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
Gigg, Martyn Anthony
committed
#include "MantidAPI/Algorithm.h"
#include "MantidAPI/AlgorithmHistory.h"
#include "MantidAPI/HistoryView.h"
#include "MantidAPI/WorkspaceHistory.h"
#include "MantidKernel/EnvironmentHistory.h"
#include <boost/algorithm/string/split.hpp>
#include "Poco/DateTime.h"
#include <Poco/DateTimeParser.h>
using Mantid::Kernel::EnvironmentHistory;
using boost::algorithm::split;
namespace Mantid {
namespace API {
namespace {
/// static logger object
Kernel::Logger g_log("WorkspaceHistory");
}
Russell Taylor
committed
/// Default Constructor
WorkspaceHistory::WorkspaceHistory()
: m_environment(),
m_algorithms(boost::bind(CompareHistory::compare, _1, _2)) {}
Russell Taylor
committed
/// Destructor
WorkspaceHistory::~WorkspaceHistory() {}
Russell Taylor
committed
Janik Zikovsky
committed
/**
Russell Taylor
committed
Standard Copy Constructor
Janik Zikovsky
committed
@param A :: WorkspaceHistory Item to copy
Russell Taylor
committed
*/
WorkspaceHistory::WorkspaceHistory(const WorkspaceHistory &A)
: m_environment(A.m_environment),
m_algorithms(boost::bind(CompareHistory::compare, _1, _2)) {
m_algorithms = A.m_algorithms;
}
Russell Taylor
committed
/// Returns a const reference to the algorithmHistory
const Mantid::API::AlgorithmHistories &
WorkspaceHistory::getAlgorithmHistories() const {
Russell Taylor
committed
return m_algorithms;
}
const Kernel::EnvironmentHistory &
WorkspaceHistory::getEnvironmentHistory() const {
Gigg, Martyn Anthony
committed
return m_environment;
}
Gigg, Martyn Anthony
committed
/// Append the algorithm history from another WorkspaceHistory into this one
void WorkspaceHistory::addHistory(const WorkspaceHistory &otherHistory) {
Russell Taylor
committed
// Don't copy one's own history onto oneself
return;
Russell Taylor
committed
}
// Merge the histories
const AlgorithmHistories &otherAlgorithms =
otherHistory.getAlgorithmHistories();
m_algorithms.insert(otherAlgorithms.begin(), otherAlgorithms.end());
Russell Taylor
committed
}
Russell Taylor
committed
/// Append an AlgorithmHistory to this WorkspaceHistory
void WorkspaceHistory::addHistory(AlgorithmHistory_sptr algHistory) {
m_algorithms.insert(algHistory);
Russell Taylor
committed
}
/*
Return the history length
*/
size_t WorkspaceHistory::size() const { return m_algorithms.size(); }
/**
* Query if the history is empty or not
* @returns True if the list is empty, false otherwise
*/
bool WorkspaceHistory::empty() const { return m_algorithms.empty(); }
/**
* Empty the list of algorithm history objects.
*/
void WorkspaceHistory::clearHistory() { m_algorithms.clear(); }
Gigg, Martyn Anthony
committed
/**
* Retrieve an algorithm history by index
* @param index :: An index within the workspace history
* @returns A pointer to an AlgorithmHistory object
* @throws std::out_of_range error if the index is invalid
Gigg, Martyn Anthony
committed
*/
AlgorithmHistory_const_sptr
WorkspaceHistory::getAlgorithmHistory(const size_t index) const {
if (index >= this->size()) {
throw std::out_of_range(
"WorkspaceHistory::getAlgorithmHistory() - Index out of range");
Gigg, Martyn Anthony
committed
}
AlgorithmHistories::const_iterator start = m_algorithms.begin();
std::advance(start, index);
return *start;
}
/**
* Index operator[] access to a workspace history
* @param index :: An index within the workspace history
* @returns A pointer to an AlgorithmHistory object
* @throws std::out_of_range error if the index is invalid
*/
AlgorithmHistory_const_sptr WorkspaceHistory::
operator[](const size_t index) const {
return getAlgorithmHistory(index);
Gigg, Martyn Anthony
committed
}
/**
* Create an algorithm from a history record at a given index
* @param index :: An index within the workspace history
* @returns A shared pointer to an algorithm object
*/
boost::shared_ptr<IAlgorithm>
WorkspaceHistory::getAlgorithm(const size_t index) const {
return Algorithm::fromHistory(*(this->getAlgorithmHistory(index)));
Gigg, Martyn Anthony
committed
}
/**
* Convenience function for retrieving the last algorithm
* @returns A shared pointer to the algorithm
*/
boost::shared_ptr<IAlgorithm> WorkspaceHistory::lastAlgorithm() const {
if (m_algorithms.empty()) {
throw std::out_of_range(
"WorkspaceHistory::lastAlgorithm() - History contains no algorithms.");
Gigg, Martyn Anthony
committed
}
return this->getAlgorithm(this->size() - 1);
}
Russell Taylor
committed
/** Prints a text representation of itself
Janik Zikovsky
committed
* @param os :: The ouput stream to write to
* @param indent :: an indentation value to make pretty printing of object and
* sub-objects
Russell Taylor
committed
*/
void WorkspaceHistory::printSelf(std::ostream &os, const int indent) const {
os << std::string(indent, ' ') << m_environment << std::endl;
AlgorithmHistories::const_iterator it;
os << std::string(indent, ' ') << "Histories:" << std::endl;
for (it = m_algorithms.begin(); it != m_algorithms.end(); ++it) {
Russell Taylor
committed
os << std::endl;
(*it)->printSelf(os, indent + 2);
Russell Taylor
committed
}
}
//------------------------------------------------------------------------------------------------
/** Saves all of the workspace history to a "process" field
* in an open NXS file.
* Code taken from NexusFileIO.cpp on May 14, 2012.
*
* @param file :: previously opened NXS file.
*/
void WorkspaceHistory::saveNexus(::NeXus::File *file) const {
file->makeGroup("process", "NXprocess", true);
// Environment history
EnvironmentHistory envHist;
output << envHist;
time_t now;
time(&now);
strftime(buffer, 25, "%Y-%b-%d %H:%M:%S", localtime(&now));
file->makeGroup("MantidEnvironment", "NXnote", true);
file->writeData("author", "mantid");
file->openData("author");
file->putAttr("date", std::string(buffer));
file->closeData();
file->writeData("description", "Mantid Environment data");
file->writeData("data", output.str());
file->closeGroup();
// Algorithm History
int algCount = 0;
AlgorithmHistories::const_iterator histIter = m_algorithms.begin();
for (; histIter != m_algorithms.end(); ++histIter) {
(*histIter)->saveNexus(file, algCount);
file->closeGroup();
}
//-------------------------------------------------------------------------------------------------
/** If the first string contains exactly three words separated by spaces
* these words will be copied into each of the following strings that were
* passed
* @param[in] words3 a string with 3 words separated by spaces
* @param[out] w1 the first word in the input string
* @param[out] w2 the second word in the input string
* @param[out] w3 the third word in the input string
* @throw out_of_range if there aren't exaltly three strings in the word
*/
void getWordsInString(const std::string &words3, std::string &w1,
std::string &w2, std::string &w3) {
Poco::StringTokenizer data(words3, " ", Poco::StringTokenizer::TOK_TRIM);
if (data.count() != 3)
throw std::out_of_range("Algorithm list line " + words3 +
" is not of the correct format\n");
w1 = data[0];
w2 = data[1];
w3 = data[2];
}
//-------------------------------------------------------------------------------------------------
/** If the first string contains exactly four words separated by spaces
* these words will be copied into each of the following strings that were
* passed
* @param[in] words4 a string with 4 words separated by spaces
* @param[out] w1 the first word in the input string
* @param[out] w2 the second word in the input string
* @param[out] w3 the third word in the input string
* @param[out] w4 the fourth word in the input string
* @throw out_of_range if there aren't exaltly four strings in the word
*/
void getWordsInString(const std::string &words4, std::string &w1,
std::string &w2, std::string &w3, std::string &w4) {
Poco::StringTokenizer data(words4, " ", Poco::StringTokenizer::TOK_TRIM);
if (data.count() != 4)
throw std::out_of_range("Algorithm list line " + words4 +
" is not of the correct format\n");
w1 = data[0];
w2 = data[1];
w3 = data[2];
w4 = data[3];
}
//------------------------------------------------------------------------------------------------
/** Opens a group called "process" and loads the workspace history from
* it.
*
* @param file :: previously opened NXS file.
*/
void WorkspaceHistory::loadNexus(::NeXus::File *file) {
// Warn but continue if the group does not exist.
file->openGroup("process", "NXprocess");
} catch (std::exception &) {
g_log.warning() << "Error opening the algorithm history field 'process'. "
"Workspace will have no history."
<< "\n";
loadNestedHistory(file);
file->closeGroup();
}
/** Load every algorithm history object at this point in the hierarchy.
* This method will recurse over every algorithm entry in the nexus file and
* load both the record and its children.
* @param file :: The handle to the nexus file
* @param parent :: Pointer to the parent AlgorithmHistory object. If null then
*loaded histories are added to
* the workspace history.
*/
void WorkspaceHistory::loadNestedHistory(::NeXus::File *file,
AlgorithmHistory_sptr parent) {
// historyNumbers should be sorted by number
std::set<int> historyNumbers = findHistoryEntries(file);
for (auto it = historyNumbers.begin(); it != historyNumbers.end(); ++it) {
std::string entryName = "MantidAlgorithm_" + Kernel::Strings::toString(*it);
std::string rawData;
file->openGroup(entryName, "NXnote");
file->readData("data", rawData);
AlgorithmHistory_sptr history = parseAlgorithmHistory(rawData);
loadNestedHistory(file, history);
parent->addChildHistory(history);
} else {
// if not parent point is supplied, assume we're at the top
// and attach the history to the workspace
this->addHistory(history);
}
} catch (std::runtime_error &e) {
// just log the exception as a warning and continue parsing history
g_log.warning() << e.what() << "\n";
}
file->closeGroup();
/** Find all the algorithm entries at a particular point the the nexus file
* @param file :: The handle to the nexus file
* @returns set of integers. One for each algorithm at the level in the file.
*/
std::set<int> WorkspaceHistory::findHistoryEntries(::NeXus::File *file) {
std::map<std::string, std::string> entries;
file->getEntries(entries);
// Histories are numbered MantidAlgorithm_0, ..., MantidAlgorithm_10, etc.
// Find all the unique numbers
for (auto it = entries.begin(); it != entries.end(); ++it) {
std::string entryName = it->first;
if (entryName.find("MantidAlgorithm_") != std::string::npos) {
entryName = entryName.substr(16, entryName.size() - 16);
int num = -1;
if (Kernel::Strings::convert(entryName, num))
historyNumbers.insert(num);
}
}
/** Parse an algorithm history entry loaded from file.
* @param rawData :: The string containing the history entry loaded from file
* @returns a pointer to the loaded algorithm history object
* @throws std::runtime_error if the loaded data could not be parsed
*/
AlgorithmHistory_sptr
WorkspaceHistory::parseAlgorithmHistory(const std::string &rawData) {
/// specifies the order that algorithm data is listed in workspaces' histories
enum AlgorithmHist {
NAME = 0, //< algorithms name
EXEC_TIME = 1, //< when the algorithm was run
EXEC_DUR = 2, //< execution time for the algorithm
PARAMS = 3 //< the algorithm's parameters
std::vector<std::string> info;
boost::split(info, rawData, boost::is_any_of("\n"));
const size_t nlines = info.size();
if (nlines < 4) { // ignore badly formed history entries
throw std::runtime_error(
"Malformed history record: Incorrect record size.");
std::string algName, dummy, temp;
// get the name and version of the algorithm
getWordsInString(info[NAME], dummy, algName, temp);
// Chop of the v from the version string
size_t numStart = temp.find('v');
// this doesn't abort if the version string doesn't contain a v
numStart = numStart != 1 ? 1 : 0;
temp = std::string(temp.begin() + numStart, temp.end());
const int version = boost::lexical_cast<int>(temp);
std::string date, time;
getWordsInString(info[EXEC_TIME], dummy, dummy, date, time);
Poco::DateTime start_timedate;
// This is needed by the Poco parsing function
Mantid::Kernel::DateAndTime utc_start;
if (!Poco::DateTimeParser::tryParse("%Y-%b-%d %H:%M:%S", date + " " + time,
start_timedate, tzdiff)) {
g_log.warning() << "Error parsing start time in algorithm history entry."
<< "\n";
utc_start = Kernel::DateAndTime::defaultTime();
getWordsInString(info[EXEC_DUR], dummy, dummy, temp, dummy);
double dur = boost::lexical_cast<double>(temp);
g_log.warning() << "Error parsing duration in algorithm history entry."
<< "\n";
dur = -1.0;
// Convert the timestamp to time_t to DateAndTime
utc_start.set_from_time_t(start_timedate.timestamp().epochTime());
// Create the algorithm history
API::AlgorithmHistory alg_hist(algName, version, utc_start, dur,
Algorithm::g_execCount);
// Simulate running an algorithm
++Algorithm::g_execCount;
// Add property information
for (size_t index = static_cast<size_t>(PARAMS) + 1; index < nlines;
++index) {
const std::string line = info[index];
std::string::size_type colon = line.find(":");
std::string::size_type comma = line.find(",");
// Each colon has a space after it
std::string prop_name = line.substr(colon + 2, comma - colon - 2);
colon = line.find(":", comma);
comma = line.find(", Default?", colon);
std::string prop_value = line.substr(colon + 2, comma - colon - 2);
colon = line.find(":", comma);
comma = line.find(", Direction", colon);
std::string is_def = line.substr(colon + 2, comma - colon - 2);
colon = line.find(":", comma);
comma = line.find(",", colon);
std::string direction = line.substr(colon + 2, comma - colon - 2);
unsigned int direc(Mantid::Kernel::Direction::asEnum(direction));
alg_hist.addProperty(prop_name, prop_value, (is_def[0] == 'Y'), direc);
}
AlgorithmHistory_sptr history =
boost::make_shared<AlgorithmHistory>(alg_hist);
//-------------------------------------------------------------------------------------------------
/** Create a flat view of the workspaces algorithm history
*/
boost::shared_ptr<HistoryView> WorkspaceHistory::createView() const {
return boost::make_shared<HistoryView>(*this);
//------------------------------------------------------------------------------------------------
Russell Taylor
committed
/** Prints a text representation
Janik Zikovsky
committed
* @param os :: The ouput stream to write to
* @param WH :: The WorkspaceHistory to output
Russell Taylor
committed
* @returns The ouput stream
*/
std::ostream &operator<<(std::ostream &os, const WorkspaceHistory &WH) {
Russell Taylor
committed
WH.printSelf(os);
return os;
}
Russell Taylor
committed
} // namespace API
} // namespace Mantid