Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #5125 - Add versionModified, repo, org, releaseTag to BCLSearchResult #5126

Merged
merged 4 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_;
}
}
}
Comment on lines +450 to +467
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grab 'em

}

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;
Comment on lines +184 to +187
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New functions for BCLSearchResults


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_TRUE(response.provenanceRequired());
jmarrec marked this conversation as resolved.
Show resolved Hide resolved
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());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it equal then?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://bcl.nrel.gov/api/search/?fq=uuid:a25386cd-60e4-46bc-8b11-c755f379d916&all_content_versions=1

Currently that's v0.9.0. But this is going to change every time the openstudio_results is updated, so I don't want to pin it and have to update the test


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New test

Loading