Skip to content

Commit

Permalink
Merge pull request #5126 from NREL/5125_BCLSearchResult_Improve
Browse files Browse the repository at this point in the history
Fix #5125 - Add versionModified, repo, org, releaseTag to BCLSearchResult
  • Loading branch information
jmarrec authored Mar 27, 2024
2 parents c66049b + 8dc8a9c commit 1b653ae
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 60 deletions.
74 changes: 58 additions & 16 deletions src/utilities/bcl/BCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "../core/Assert.hpp"
#include "../core/StringHelpers.hpp"
#include "../time/DateTime.hpp"

#include <pugixml.hpp>

Expand Down Expand Up @@ -128,24 +129,33 @@ BCLFile::BCLFile(const pugi::xml_node& fileElement) {

m_softwareProgram = softwareProgramElement.text().as_string();
m_identifier = identifierElement.text().as_string();
if (!minCompatibleElement) {
try {
// if minCompatibleVersion not explicitly set, assume identifier is min
m_minCompatibleVersion = VersionString(m_identifier);
} catch (const std::exception&) {

// Avoids gettings lots of 'Could not parse '' as a version string'
auto parseVersionIfExistsAndNotEmpty = [](const pugi::xml_node& vElement) -> boost::optional<VersionString> {
if (!vElement) {
return boost::none;
}
} else {
try {
m_minCompatibleVersion = VersionString(minCompatibleElement.text().as_string());
} catch (const std::exception&) {
const std::string vstr = vElement.text().as_string();
if (vstr.empty()) {
return boost::none;
}
}
if (maxCompatibleElement) {
boost::optional<VersionString> result;

try {
m_maxCompatibleVersion = VersionString(maxCompatibleElement.text().as_string());
result = VersionString(vstr);
} catch (const std::exception&) {
}
return result;
};

if (!minCompatibleElement) {
m_minCompatibleVersion = parseVersionIfExistsAndNotEmpty(identifierElement);
} else {
m_minCompatibleVersion = parseVersionIfExistsAndNotEmpty(minCompatibleElement);
}

m_maxCompatibleVersion = parseVersionIfExistsAndNotEmpty(maxCompatibleElement);

m_filename = filenameElement.text().as_string();
m_url = urlElement.text().as_string();
m_filetype = filetypeElement.text().as_string();
Expand Down Expand Up @@ -350,7 +360,7 @@ BCLSearchResult::BCLSearchResult(const pugi::xml_node& componentElement) : m_com
auto provenanceElement = provenancesElement.child("provenance");
while (provenanceElement) {
if (provenanceElement.first_child() != nullptr) {
m_provenances.push_back(BCLProvenance(provenanceElement));
m_provenances.emplace_back(provenanceElement);
} else {
break;
}
Expand All @@ -359,7 +369,7 @@ BCLSearchResult::BCLSearchResult(const pugi::xml_node& componentElement) : m_com
auto provenanceRequiredElement = provenancesElement.child("provenance_required");
if (provenanceRequiredElement) {
std::string required = provenanceRequiredElement.text().as_string();
m_provenanceRequired = (required == "true") ? true : false;
m_provenanceRequired = (required == "true");
}

auto tagElement = tagsElement.child("tag");
Expand Down Expand Up @@ -418,7 +428,7 @@ BCLSearchResult::BCLSearchResult(const pugi::xml_node& componentElement) : m_com
auto fileElement = filesElement.child("file");
while (fileElement) {
if (fileElement.first_child() != nullptr) {
m_files.push_back(BCLFile(fileElement));
m_files.emplace_back(fileElement); // BCLFile
} else {
break;
}
Expand All @@ -429,13 +439,32 @@ BCLSearchResult::BCLSearchResult(const pugi::xml_node& componentElement) : m_com
auto costElement = costsElement.child("cost");
while (costElement) {
if (costElement.first_child() != nullptr) {
m_costs.push_back(BCLCost(costElement));
m_costs.emplace_back(costElement); // BCLCost
} else {
break;
}
costElement = costElement.next_sibling("cost");
}
}

if (auto orgElement = componentElement.child("org")) {
m_org = orgElement.text().as_string();
}
if (auto repoElement = componentElement.child("repo")) {
m_repo = repoElement.text().as_string();
}
if (auto release_tagElement = componentElement.child("release_tag")) {
m_releaseTag = release_tagElement.text().as_string();
}
if (auto versionModifiedElement = componentElement.child("version_modified")) {
const std::string versionModified = versionModifiedElement.text().as_string();
if (!versionModified.empty()) {
// fromXsdDateTime forwards to fromISO8601 and handles both formats
if (auto dt_ = openstudio::DateTime::fromXsdDateTime(versionModified)) {
m_versionModified = *dt_;
}
}
}
}

std::string BCLSearchResult::uid() const {
Expand Down Expand Up @@ -490,6 +519,19 @@ std::vector<BCLCost> BCLSearchResult::costs() const {
return m_costs;
}

std::string BCLSearchResult::org() const {
return m_org;
}
std::string BCLSearchResult::repo() const {
return m_repo;
}
std::string BCLSearchResult::releaseTag() const {
return m_releaseTag;
}
boost::optional<openstudio::DateTime> BCLSearchResult::versionModified() const {
return m_versionModified;
}

BCL::BCL() = default;

boost::optional<BCLComponent> getComponent(const std::string& uid, const std::string& versionId) {
Expand Down
11 changes: 11 additions & 0 deletions src/utilities/bcl/BCL.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "../core/Path.hpp"
#include "../core/Logger.hpp"
#include "../data/Attribute.hpp"
#include "../time/DateTime.hpp"

namespace pugi {
class xml_node;
Expand Down Expand Up @@ -180,6 +181,11 @@ class UTILITIES_API BCLSearchResult
std::vector<BCLFile> files() const;
std::vector<BCLCost> costs() const;

std::string org() const;
std::string repo() const;
std::string releaseTag() const;
boost::optional<openstudio::DateTime> versionModified() const;

private:
REGISTER_LOGGER("openstudio.BCLSearchResult");

Expand All @@ -196,6 +202,11 @@ class UTILITIES_API BCLSearchResult
std::vector<Attribute> m_attributes;
std::vector<BCLFile> m_files;
std::vector<BCLCost> m_costs;

std::string m_org;
std::string m_repo;
std::string m_releaseTag;
boost::optional<openstudio::DateTime> m_versionModified;
};

/// This is a generic interface that can be used for searching either the local or remote bcl.
Expand Down
86 changes: 42 additions & 44 deletions src/utilities/bcl/BCLXML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,27 @@ BCLXML::BCLXML(const openstudio::path& xmlPath) : m_path(openstudio::filesystem:
}
}

// get schema version to see if we need to upgrade anything
// added in schema version 3
VersionString startingVersion("2.0");
auto subelement = element.child("schema_version");
if (subelement) {
// Avoids gettings lots of 'Could not parse '' as a version string'
auto parseVersionIfExistsAndNotEmpty = [](const pugi::xml_node& vElement) -> boost::optional<VersionString> {
if (!vElement) {
return boost::none;
}
const std::string vstr = vElement.text().as_string();
if (vstr.empty()) {
return boost::none;
}
boost::optional<VersionString> result;

try {
startingVersion = VersionString(subelement.text().as_string());
result = VersionString(vstr);
} catch (const std::exception&) {
// Yuck
}
}
return result;
};

// get schema version to see if we need to upgrade anything
// added in schema version 3
const VersionString startingVersion = parseVersionIfExistsAndNotEmpty(element.child("schema_version")).get_value_or(VersionString("2.0"));

// validate the gbxml prior to reverse translation
auto bclXMLValidator = XMLValidator::bclXMLValidator(m_bclXMLType, startingVersion);
Expand All @@ -101,12 +111,14 @@ BCLXML::BCLXML(const openstudio::path& xmlPath) : m_path(openstudio::filesystem:
m_error = errorElement.text().as_string();
}

subelement = element.child("version_modified");
if (subelement) {
if (auto subelement = element.child("version_modified")) {
m_versionModified = subelement.text().as_string();
if (!DateTime::fromXsdDateTime(m_versionModified)) {
// not an allowable date time
m_versionModified = "";
if (m_versionModified.empty()) {
// fromXsdDateTime forwards to fromISO8601 and handles both formats
if (!DateTime::fromXsdDateTime(m_versionModified)) {
// not an allowable date time
m_versionModified = "";
}
}
}

Expand All @@ -127,8 +139,7 @@ BCLXML::BCLXML(const openstudio::path& xmlPath) : m_path(openstudio::filesystem:
if (m_bclXMLType == BCLXMLType::MeasureXML) {
m_modelerDescription = decodeString(element.child("modeler_description").text().as_string());

subelement = element.child("arguments");
if (subelement) {
if (auto subelement = element.child("arguments")) {
for (auto& arg : subelement.children("argument")) {
try {
m_arguments.emplace_back(arg);
Expand All @@ -138,8 +149,7 @@ BCLXML::BCLXML(const openstudio::path& xmlPath) : m_path(openstudio::filesystem:
}
}

subelement = element.child("outputs");
if (subelement) {
if (auto subelement = element.child("outputs")) {
for (auto& outputElement : subelement.children("output")) {
if (outputElement.first_child() != nullptr) {
try {
Expand All @@ -152,8 +162,7 @@ BCLXML::BCLXML(const openstudio::path& xmlPath) : m_path(openstudio::filesystem:
}
}

subelement = element.child("files");
if (subelement) {
if (auto subelement = element.child("files")) {
for (auto& fileElement : subelement.children("file")) {
if (fileElement.first_child() != nullptr) {

Expand All @@ -163,32 +172,23 @@ BCLXML::BCLXML(const openstudio::path& xmlPath) : m_path(openstudio::filesystem:
boost::optional<VersionString> maxCompatibleVersion;
auto versionElement = fileElement.child("version");
if (versionElement) {
softwareProgram = versionElement.child("software_program").text().as_string();
softwareProgramVersion = versionElement.child("identifier").text().as_string();
auto softwareProgramElement = versionElement.child("software_program");
softwareProgram = softwareProgramElement.text().as_string();
auto softwareProgramVersionElement = versionElement.child("identifier");
softwareProgramVersion = softwareProgramVersionElement.text().as_string();

// added in schema version 3
auto minCompatibleVersionElement = versionElement.child("min_compatible");

if (!minCompatibleVersionElement) {
try {
// if minCompatibleVersion not explicitly set, assume softwareProgramVersion is min
minCompatibleVersion = VersionString(softwareProgramVersion);
} catch (const std::exception&) {
}
minCompatibleVersion = parseVersionIfExistsAndNotEmpty(softwareProgramVersionElement);
} else {
try {
minCompatibleVersion = VersionString(minCompatibleVersionElement.text().as_string());
} catch (const std::exception&) {
}
minCompatibleVersion = parseVersionIfExistsAndNotEmpty(minCompatibleVersionElement);
}

// added in schema version 3
auto maxCompatibleVersionElement = versionElement.child("max_compatible");
if (maxCompatibleVersionElement) {
try {
maxCompatibleVersion = VersionString(maxCompatibleVersionElement.text().as_string());
} catch (const std::exception&) {
}
}
maxCompatibleVersion = parseVersionIfExistsAndNotEmpty(maxCompatibleVersionElement);
}
const std::string fileName = fileElement.child("filename").text().as_string();
//std::string fileType = fileElement.firstChildElement("filetype").firstChild().nodeValue().toStdString();
Expand Down Expand Up @@ -229,16 +229,15 @@ BCLXML::BCLXML(const openstudio::path& xmlPath) : m_path(openstudio::filesystem:
}
}

subelement = element.child("attributes");
if (subelement) {
if (auto subelement = element.child("attributes")) {
for (auto& attributeElement : subelement.children("attribute")) {
if (attributeElement.first_child() != nullptr) {
std::string name = attributeElement.child("name").text().as_string();
std::string value = attributeElement.child("value").text().as_string();
std::string datatype = attributeElement.child("datatype").text().as_string();
const std::string name = attributeElement.child("name").text().as_string();
const std::string value = attributeElement.child("value").text().as_string();
const std::string datatype = attributeElement.child("datatype").text().as_string();

// Units are optional
std::string units = attributeElement.child("units").text().as_string();
const std::string units = attributeElement.child("units").text().as_string();

if (datatype == "float") {
if (units.empty()) {
Expand Down Expand Up @@ -273,8 +272,7 @@ BCLXML::BCLXML(const openstudio::path& xmlPath) : m_path(openstudio::filesystem:
}
}

subelement = element.child("tags");
if (subelement) {
if (auto subelement = element.child("tags")) {
for (auto& tagElement : subelement.children("tag")) {
auto text = tagElement.text();
if (!text.empty()) {
Expand Down
42 changes: 42 additions & 0 deletions src/utilities/bcl/test/BCL_GTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,45 @@ TEST_F(BCLFixture, RemoteBCL_EncodingURI) {
std::vector<BCLSearchResult> responses = remoteBCL.searchComponentLibrary("ashrae 4A", 127);
ASSERT_GT(responses.size(), 0u);
}

TEST_F(BCLFixture, RemoteBCL_BCLSearchResult) {
RemoteBCL remoteBCL;

const std::string openstudio_results_uid = "a25386cd-60e4-46bc-8b11-c755f379d916";
// get openstudio_results
// std::vector<BCLSearchResult> responses = remoteBCL.searchMeasureLibrary("openstudio_results", 980);
std::vector<BCLSearchResult> responses = remoteBCL.searchMeasureLibrary(openstudio_results_uid, 980);

ASSERT_EQ(1, responses.size());
auto& response = responses.front();

EXPECT_FALSE(response.name().empty());
EXPECT_EQ("Openstudio results", response.name());

EXPECT_FALSE(response.uid().empty());
EXPECT_EQ(openstudio_results_uid, response.uid());

EXPECT_FALSE(response.versionId().empty());
EXPECT_FALSE(response.description().empty());
EXPECT_FALSE(response.modelerDescription().empty());
EXPECT_TRUE(response.fidelityLevel().empty());
EXPECT_EQ("measure", response.componentType());

EXPECT_FALSE(response.provenanceRequired());
EXPECT_TRUE(response.provenances().empty());
EXPECT_FALSE(response.tags().empty());
EXPECT_FALSE(response.attributes().empty());
EXPECT_FALSE(response.files().empty());
EXPECT_TRUE(response.costs().empty());

EXPECT_FALSE(response.org().empty());
EXPECT_EQ("NREL", response.org());
EXPECT_FALSE(response.repo().empty());
EXPECT_EQ("openstudio-common-measures-gem", response.repo());
EXPECT_FALSE(response.releaseTag().empty());

auto dt_ = response.versionModified();
ASSERT_TRUE(dt_);
const openstudio::DateTime dateTime(Date(MonthOfYear::Nov, 14, 2022));
EXPECT_GT(*dt_, dateTime);
}

0 comments on commit 1b653ae

Please sign in to comment.