13#include <boost/algorithm/string/predicate.hpp>
16#include <Poco/DateTimeFormat.h>
17#include <Poco/DateTimeFormatter.h>
18#include <Poco/DirectoryIterator.h>
23#if defined(_WIN32) || defined(_WIN64)
25#pragma warning(disable : 4250)
26#include <Poco/FileStream.h>
27#include <Poco/NullStream.h>
31#include <Poco/FileStream.h>
32#include <Poco/NullStream.h>
43using namespace Kernel;
67 return "Checks the Mantid instrument repository against the local "
68 "instrument files, and downloads updates as appropriate.";
94 g_log.
debug() <<
"Unable to get the rate limit from GitHub: " << ex.
what() <<
'\n';
100 std::string errorText(ex.
what());
101 if (errorText.find(
"rate limit") != std::string::npos) {
102 g_log.
information() <<
"Instrument Definition Update: " << errorText <<
'\n';
105 g_log.
notice(
"Internet Connection Failed - cannot update instrument "
106 "definitions. Please check your connection. If you are behind a "
107 "proxy server, consider setting proxy.host and proxy.port in "
108 "the Mantid properties file or using the config object.");
115 if (fileMap.empty()) {
116 g_log.
notice(
"All instrument definitions up to date");
118 std::string s = (fileMap.size() > 1) ?
"s" :
"";
119 g_log.
notice() <<
"Downloading " << fileMap.size() <<
" file" << s <<
" from the instrument repository\n";
122 for (
auto &itMap : fileMap) {
124 if (boost::algorithm::ends_with(itMap.second,
"Facilities.xml")) {
125 g_log.
notice(
"A new Facilities.xml file has been downloaded, this will "
126 "take effect next time Mantid is started.");
128 g_log.
information() <<
"Downloading \"" << itMap.second <<
"\" from \"" << itMap.first <<
"\"\n";
133 setProperty(
"FileDownloadCount",
static_cast<int>(fileMap.size()));
138std::string getDownloadUrl(Json::Value &contents) {
139 std::string url = contents.get(
"download_url",
"").asString();
141 url = contents.get(
"html_url",
"").asString();
143 throw std::runtime_error(
"Failed to find download link");
144 url = url +
"?raw=1";
154 Poco::Path installPath(instrumentDirs.back());
155 installPath.makeDirectory();
156 Poco::Path localPath(instrumentDirs[0]);
157 localPath.makeDirectory();
160 Poco::Path gitHubJson(localPath,
"github.json");
161 Poco::File gitHubJsonFile(gitHubJson);
162 Poco::DateTime gitHubJsonDate(1900, 1, 1);
163 bool forceUpdate = this->
getProperty(
"ForceUpdate");
164 if ((!forceUpdate) && gitHubJsonFile.exists() && gitHubJsonFile.isFile()) {
165 gitHubJsonDate = gitHubJsonFile.getLastModified();
170 headers.emplace(
"if-modified-since",
171 Poco::DateTimeFormatter::format(gitHubJsonDate, Poco::DateTimeFormat::HTTP_FORMAT));
173 if (gitHubInstrumentRepoUrl.empty()) {
174 throw std::runtime_error(
"Property UpdateInstrumentDefinitions.URL is not defined, "
175 "this should point to the location of the instrument "
176 "directory in the github API "
178 "https://api.github.com/repos/mantidproject/mantid/contents/"
183 doDownloadFile(gitHubInstrumentRepoUrl, gitHubJson.toString(), headers);
194 Poco::Path installRepoFile(localPath,
"install.json");
196 Poco::Path localRepoFile(localPath,
"local.json");
200 if (gitHubJsonFile.getSize() == 0) {
201 std::stringstream msg;
202 msg <<
"Encountered empty file \"" << gitHubJson.toString() <<
"\" while determining what to download";
203 throw std::runtime_error(msg.str());
207 ::Json::CharReaderBuilder readerBuilder;
208 Json::Value serverContents;
209 Poco::FileStream fileStream(gitHubJson.toString(), std::ios::in);
212 Json::parseFromStream(readerBuilder, fileStream, &serverContents, &errors);
213 if (errors.size() != 0) {
214 throw std::runtime_error(
"Unable to parse server JSON file \"" + gitHubJson.toString() +
"\"");
218 std::unordered_set<std::string> repoFilenames;
220 for (
auto &serverElement : serverContents) {
221 std::string
name = serverElement.get(
"name",
"").asString();
222 repoFilenames.insert(
name);
223 Poco::Path filePath(localPath,
name);
224 if (filePath.getExtension() !=
"xml")
226 std::string sha = serverElement.get(
"sha",
"").asString();
227 std::string downloadUrl = getDownloadUrl(serverElement);
235 if ((sha != installSha) && (sha != localSha)) {
236 fileMap.emplace(downloadUrl,
237 filePath.toString());
238 }
else if ((!localSha.empty()) && (sha == installSha) && (sha != localSha))
240 fileMap.emplace(downloadUrl, filePath.toString());
261 const std::string &key,
const std::string &defaultValue)
const {
262 auto element = mapping.find(key);
263 return (element != mapping.end()) ? element->second : defaultValue;
273 using Poco::DirectoryIterator;
274 DirectoryIterator end;
275 for (DirectoryIterator it(directoryPath); it != end; ++it) {
276 const auto &entryPath = Poco::Path(it->path());
277 if (entryPath.getExtension() !=
"xml")
281 filesToSha.emplace(entryPath.getFileName(), sha1);
283 }
catch (Poco::Exception &ex) {
284 g_log.
error() <<
"DownloadInstrument: failed to parse the directory: " << directoryPath <<
" : " << ex.className()
285 <<
" : " << ex.displayText() <<
'\n';
287 }
catch (std::exception &ex) {
288 std::stringstream ss;
289 ss <<
"unknown exception while checking local file system. " << ex.what() <<
". Input = " << directoryPath;
290 throw std::runtime_error(ss.str());
302 const std::unordered_set<std::string> &filenamesToKeep)
const {
305 std::vector<std::string> filesToDelete;
308 using Poco::DirectoryIterator;
309 DirectoryIterator end;
310 for (DirectoryIterator it(directoryPath); it != end; ++it) {
311 const auto &entryPath = Poco::Path(it->path());
312 if (entryPath.getExtension() !=
"xml")
314 if (filenamesToKeep.find(entryPath.getFileName()) == filenamesToKeep.end()) {
315 g_log.
debug() <<
"File not found in remote instrument repository, will "
317 << entryPath.getFileName() <<
'\n';
318 filesToDelete.emplace_back(it->path());
321 }
catch (Poco::Exception &ex) {
322 g_log.
error() <<
"DownloadInstrument: failed to list the directory: " << directoryPath <<
" : " << ex.className()
323 <<
" : " << ex.displayText() <<
'\n';
325 }
catch (std::exception &ex) {
326 std::stringstream ss;
327 ss <<
"unknown exception while checking local file system. " << ex.what() <<
". Input = " << directoryPath;
328 throw std::runtime_error(ss.str());
333 for (
const auto &filename : filesToDelete) {
334 Poco::File file(filename);
337 }
catch (Poco::Exception &ex) {
338 g_log.
error() <<
"DownloadInstrument: failed to delete file: " << ex.className() <<
" : " << ex.displayText()
341 }
catch (std::exception &ex) {
342 std::stringstream ss;
343 ss <<
"unknown exception while deleting file: " << ex.what();
344 throw std::runtime_error(ss.str());
347 g_log.
debug() << filesToDelete.size() <<
" Files deleted.\n";
349 return filesToDelete.size();
366 const std::string &localFilePath,
368 Poco::File localFile(localFilePath);
369 if (localFile.exists()) {
370 if (!localFile.canWrite()) {
371 std::stringstream msg;
372 msg <<
"Cannot write file \"" << localFilePath <<
"\"";
373 throw std::runtime_error(msg.str());
376 localFile = Poco::File(Poco::Path(localFilePath).parent().
toString());
377 if (!localFile.canWrite()) {
378 std::stringstream msg;
379 msg <<
"Cannot write file \"" << localFilePath <<
"\"";
380 throw std::runtime_error(msg.str());
385 inetHelper.
headers().insert(headers.begin(), headers.end());
386 const auto retStatus = inetHelper.
downloadFile(urlFile, localFilePath);
#define DECLARE_ALGORITHM(classname)
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
std::string toString() const override
Serialize an object to a string.
DownloadInstrument : Downloads one or more instrument files to the local instrument cache from the in...
void exec() override
Execute the algorithm.
int version() const override
Algorithm's version for identification.
size_t removeOrphanedFiles(const std::string &directoryPath, const std::unordered_set< std::string > &filenamesToKeep) const
removes any .xml files in a directory that are not in filenamesToKeep
StringToStringMap getFileShas(const std::string &directoryPath)
Creates or updates the json file of a directories contents.
virtual Kernel::InternetHelper::HTTPStatus doDownloadFile(const std::string &urlFile, const std::string &localFilePath="", const StringToStringMap &headers=StringToStringMap())
Download a url and fetch it inside the local path given.
std::map< std::string, std::string > StringToStringMap
void init() override
Initialize the algorithm's properties.
const std::string name() const override
Algorithms name for identification.
std::string getValueOrDefault(const StringToStringMap &mapping, const std::string &key, const std::string &defaultValue) const
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
const std::string category() const override
Algorithm's category for identification.
StringToStringMap processRepository()
Exception thrown when error occurs accessing an internet resource.
const char * what() const noexcept override
Overloaded reporting method.
const int & errorCode() const
Writes out the range and limits.
GitHubApiHelper : A helper class for supporting access to the github api through HTTP and HTTPS,...
std::string getRateLimitDescription()
String describing the rate limit status.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
StringToStringMap & headers()
Returns a reference to the headers map.
virtual HTTPStatus downloadFile(const std::string &urlFile, const std::string &localFilePath="")
Download a url and fetch it inside the local path given.
void debug(const std::string &msg)
Logs at debug level.
void notice(const std::string &msg)
Logs at notice level.
void error(const std::string &msg)
Logs at error level.
void information(const std::string &msg)
Logs at information level.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
MANTID_KERNEL_DLL std::string gitSha1FromFile(const std::string &filepath)
create a git checksum from a file (these match the git hash-object command)
Describes the direction (within an algorithm) of a Property.
@ Output
An output workspace.