<commit_before>/**
 * Copyright (c) 2016 zScale Technology GmbH <legal@zscale.io>
 * Authors:
 *   - Paul Asmuth <paul@zscale.io>
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License ("the license") as
 * published by the Free Software Foundation, either version 3 of the License,
 * or any later version.
 *
 * In accordance with Section 7(e) of the license, the licensing of the Program
 * under the license does not imply a trademark license. Therefore any rights,
 * title and interest in our trademarks remain entirely with us.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the license for more details.
 *
 * You can be released from the requirements of the license by purchasing a
 * commercial license. Buying such a license is mandatory as soon as you develop
 * commercial activities involving this program without disclosing the source
 * code of your own applications
 */
#include <eventql/cli/console.h>
#include <eventql/util/inspect.h>
#include "eventql/util/logging.h"
#include "eventql/util/random.h"
#include "eventql/util/util/Base64.h"
#include "eventql/util/thread/eventloop.h"
#include "eventql/util/thread/threadpool.h"
#include "eventql/util/thread/FixedSizeThreadPool.h"
#include "eventql/util/io/TerminalOutputStream.h"
#include "eventql/util/wallclock.h"
#include "eventql/util/json/json.h"
#include "eventql/util/json/jsonrpc.h"
#include "eventql/util/http/httpclient.h"
#include "eventql/util/http/HTTPSSEResponseHandler.h"
#include "eventql/util/cli/CLI.h"
#include "eventql/util/cli/flagparser.h"
#include "eventql/util/cli/term.h"
#include "eventql/server/sql/codec/binary_codec.h"
#include "eventql/sql/result_list.h"
#include "linenoise/linenoise.h"

namespace eventql {
namespace cli {

void Console::startInteractiveShell() {
  char *p;
  while ((p = linenoise("evql> ")) != NULL) {
    String line(p);
    linenoiseFree(p);

    if (line == "quit") {
      return;

    } else {
      runQuery(line);
    }
  }
}

Console::Console(const CLIConfig cli_cfg) : cfg_(cli_cfg) {}

Status Console::runQuery(const String& query) {
  if (cfg_.getBatchMode()) {
    return runQueryBatch(query);
  } else {
    return runQueryTable(query);
  }
}

Status Console::runQueryTable(const String& query) {
  auto stdout_os = TerminalOutputStream::fromStream(OutputStream::getStdout());
  auto stderr_os = TerminalOutputStream::fromStream(OutputStream::getStderr());
  bool line_dirty = false;
  bool is_tty = stderr_os->isTTY();

  bool error = false;
  csql::ResultList results;
  auto res_parser = new csql::BinaryResultParser();

  res_parser->onProgress([&stdout_os, &line_dirty, is_tty] (
      const csql::ExecutionStatus& status) {
    auto status_line = StringUtil::format(
        "Query running: $0%",
        status.progress * 100);

    if (is_tty) {
      stdout_os->eraseLine();
      stdout_os->print("\r" + status_line);
      line_dirty = true;
    } else {
      stdout_os->print(status_line + "\n");
    }

  });

  res_parser->onTableHeader([&results] (const Vector<String>& columns) {
    results.addHeader(columns);
  });

  res_parser->onRow([&results] (int argc, const csql::SValue* argv) {
    results.addRow(argv, argc);
  });

  res_parser->onError([&stderr_os, &error] (const String& error_str) {
    stderr_os->print(
        "ERROR:",
        { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(StringUtil::format(" $0\n", error_str));
    error = true;
  });

  auto res = sendRequest(query, res_parser);
  if (!res.isSuccess()) {
    stderr_os->print(
          "ERROR:",
          { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(res.message());
    return res;
  }

  if (is_tty) {
    stderr_os->eraseLine();
    stderr_os->print("\r");
  }

  if (error) {
    return Status(eIOError);
  }

  if (results.getNumRows() > 0) {
    results.debugPrint();

    auto num_rows = results.getNumRows();
    auto status_line = StringUtil::format(
        "$0 row$1 returned",
        num_rows,
        num_rows > 1 ? "s" : "");

    if (is_tty) {
      stderr_os->print("\r" + status_line + "\n\n");
    } else {
      stderr_os->print(status_line + "\n\n");
    }

  } else {
    String line = is_tty ? "\r" : "";
    if (results.getNumColumns() == 0) {
      stderr_os->print(line + "Query OK \n\n");
    } else {
      stderr_os->print(line + "Empty set \n\n");
    }
  }

  return Status::success();
}

Status Console::runQueryBatch(const String& query) {
  auto stdout_os = TerminalOutputStream::fromStream(OutputStream::getStdout());
  auto stderr_os = TerminalOutputStream::fromStream(OutputStream::getStderr());
  bool line_dirty = false;
  bool is_tty = stderr_os->isTTY();

  bool error = false;
  csql::ResultList results;
  auto res_parser = new csql::BinaryResultParser();

  res_parser->onTableHeader([&stdout_os] (const Vector<String>& columns) {
    for (const auto col : columns) {
      stdout_os->print(col + "\t");
    }
    stdout_os->print("\n");
  });

  res_parser->onRow([&stdout_os] (int argc, const csql::SValue* argv) {
    for (size_t i = 0; i < argc; ++i) {
      stdout_os->print(argv[i].getString() + "\t");
    }
    stdout_os->print("\n");
  });

  res_parser->onError([&stderr_os, &error] (const String& error_str) {
    stderr_os->print(
        "ERROR:",
        { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(StringUtil::format(" $0\n", error_str));
    error = true;
  });

  auto res = sendRequest(query, res_parser);
  if (!res.isSuccess()) {
    stderr_os->print(
          "ERROR:",
          { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(res.message());
    return res;
  }

  if (is_tty) {
    stderr_os->eraseLine();
    stderr_os->print("\r");
  }

  if (error) {
    return Status(eIOError);
  }

  return Status::success();
}

Status Console::sendRequest(const String& query, csql::BinaryResultParser* res_parser) {
  try {
    auto url = StringUtil::format(
        "http://$0:$1/api/v1/sql",
        cfg_.getHost(),
        cfg_.getPort());

    auto db = cfg_.getDatabase().isEmpty() ? "" : cfg_.getDatabase().get();
    auto postdata = StringUtil::format(
          "format=binary&query=$0&database=$1",
          URI::urlEncode(query),
          URI::urlEncode(db));

    http::HTTPMessage::HeaderList auth_headers;
    if (!cfg_.getAuthToken().isEmpty()) {
      auth_headers.emplace_back(
          "Authorization",
          StringUtil::format("Token $0", cfg_.getAuthToken().get()));
    } else if (!cfg_.getPassword().isEmpty()) {
      auth_headers.emplace_back(
          "Authorization",
          StringUtil::format("Basic $0",
              util::Base64::encode(
                  cfg_.getUser() + ":" + cfg_.getPassword().get())));
    }

    http::HTTPClient http_client(nullptr);
    auto req = http::HTTPRequest::mkPost(url, postdata, auth_headers);
    auto res = http_client.executeRequest(
        req,
        http::StreamingResponseHandler::getFactory(
            std::bind(
                &csql::BinaryResultParser::parse,
                res_parser,
                std::placeholders::_1,
                std::placeholders::_2)));

    if (!res_parser->eof()) {
      return Status(eIOError, "connection to server lost");
    }

    return Status::success();

  } catch (const StandardException& e) {
    return Status(eIOError, StringUtil::format(" $0\n", e.what()));
  }
}

Status Console::runJS(const String& program_source) {
  auto stdout_os = OutputStream::getStdout();
  auto stderr_os = TerminalOutputStream::fromStream(OutputStream::getStderr());

  try {

    bool finished = false;
    bool error = false;
    String error_string;
    bool line_dirty = false;
    bool is_tty = stderr_os->isTTY();

    auto event_handler = [&] (const http::HTTPSSEEvent& ev) {
      if (ev.name.isEmpty()) {
        return;
      }

      if (ev.name.get() == "status") {
        auto obj = json::parseJSON(ev.data);
        auto tasks_completed = json::objectGetUInt64(obj, "num_tasks_completed");
        auto tasks_total = json::objectGetUInt64(obj, "num_tasks_total");
        auto tasks_running = json::objectGetUInt64(obj, "num_tasks_running");
        auto progress = json::objectGetFloat(obj, "progress");

        auto status_line = StringUtil::format(
            "[$0/$1] $2 tasks running ($3%)",
            tasks_completed.isEmpty() ? 0 : tasks_completed.get(),
            tasks_total.isEmpty() ? 0 : tasks_total.get(),
            tasks_running.isEmpty() ? 0 : tasks_running.get(),
            progress.isEmpty() ? 0 : progress.get() * 100);

        if (is_tty) {
          stderr_os->eraseLine();
          stderr_os->print("\r" + status_line);
          line_dirty = true;
        } else {
          stderr_os->print(status_line + "\n");
        }

        return;
      }

      if (line_dirty) {
        stderr_os->eraseLine();
        stderr_os->print("\r");
        line_dirty = false;
      }

      if (ev.name.get() == "job_started") {
        //stderr_os->printYellow(">> Job started\n");
        return;
      }

      if (ev.name.get() == "job_finished") {
        finished = true;
        return;
      }

      if (ev.name.get() == "error") {
        error = true;
        error_string = URI::urlDecode(ev.data);
        return;
      }

      if (ev.name.get() == "result") {
        stdout_os->write(URI::urlDecode(ev.data) + "\n");
        return;
      }

      if (ev.name.get() == "log") {
        stderr_os->print(URI::urlDecode(ev.data) + "\n");
        return;
      }

    };

    auto url = StringUtil::format(
        "http://$0:$1/api/v1/mapreduce/execute",
        cfg_.getHost(),
        cfg_.getPort());

    http::HTTPMessage::HeaderList auth_headers;
    auth_headers.emplace_back(
        "Authorization",
        StringUtil::format("Token $0", cfg_.getAuthToken().get()));

    if (is_tty) {
      stderr_os->print("Launching job...");
      line_dirty = true;
    } else {
      stderr_os->print("Launching job...\n");
    }

    http::HTTPClient http_client(nullptr);
    auto req = http::HTTPRequest::mkPost(url, program_source, auth_headers);
    auto res = http_client.executeRequest(
        req,
        http::HTTPSSEResponseHandler::getFactory(event_handler));

    if (line_dirty) {
      stderr_os->eraseLine();
      stderr_os->print("\r");
    }

    if (res.statusCode() != 200) {
      error = true;
      error_string = "HTTP Error: " + res.body().toString();
    }

    if (!finished && !error) {
      error = true;
      error_string = "connection to server lost";
    }

    if (error) {
      stderr_os->print(
          "ERROR:",
          { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

      stderr_os->print(" " + error_string + "\n");
      return Status(eIOError);
    } else {
      stderr_os->printGreen("Job successfully completed\n");
      return Status::success();
    }
  } catch (const StandardException& e) {
    stderr_os->print(
        "ERROR:",
        { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(StringUtil::format(" $0\n", e.what()));
    return Status(eIOError);
  }

}

} // namespace cli
} // namespace eventql

<commit_msg>print query progress in batch mode unless --quiet is set<commit_after>/**
 * Copyright (c) 2016 zScale Technology GmbH <legal@zscale.io>
 * Authors:
 *   - Paul Asmuth <paul@zscale.io>
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License ("the license") as
 * published by the Free Software Foundation, either version 3 of the License,
 * or any later version.
 *
 * In accordance with Section 7(e) of the license, the licensing of the Program
 * under the license does not imply a trademark license. Therefore any rights,
 * title and interest in our trademarks remain entirely with us.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the license for more details.
 *
 * You can be released from the requirements of the license by purchasing a
 * commercial license. Buying such a license is mandatory as soon as you develop
 * commercial activities involving this program without disclosing the source
 * code of your own applications
 */
#include <eventql/cli/console.h>
#include <eventql/util/inspect.h>
#include "eventql/util/logging.h"
#include "eventql/util/random.h"
#include "eventql/util/util/Base64.h"
#include "eventql/util/thread/eventloop.h"
#include "eventql/util/thread/threadpool.h"
#include "eventql/util/thread/FixedSizeThreadPool.h"
#include "eventql/util/io/TerminalOutputStream.h"
#include "eventql/util/wallclock.h"
#include "eventql/util/json/json.h"
#include "eventql/util/json/jsonrpc.h"
#include "eventql/util/http/httpclient.h"
#include "eventql/util/http/HTTPSSEResponseHandler.h"
#include "eventql/util/cli/CLI.h"
#include "eventql/util/cli/flagparser.h"
#include "eventql/util/cli/term.h"
#include "eventql/server/sql/codec/binary_codec.h"
#include "eventql/sql/result_list.h"
#include "linenoise/linenoise.h"

namespace eventql {
namespace cli {

void Console::startInteractiveShell() {
  char *p;
  while ((p = linenoise("evql> ")) != NULL) {
    String line(p);
    linenoiseFree(p);

    if (line == "quit") {
      return;

    } else {
      runQuery(line);
    }
  }
}

Console::Console(const CLIConfig cli_cfg) : cfg_(cli_cfg) {}

Status Console::runQuery(const String& query) {
  if (cfg_.getBatchMode()) {
    return runQueryBatch(query);
  } else {
    return runQueryTable(query);
  }
}

Status Console::runQueryTable(const String& query) {
  auto stdout_os = TerminalOutputStream::fromStream(OutputStream::getStdout());
  auto stderr_os = TerminalOutputStream::fromStream(OutputStream::getStderr());
  bool line_dirty = false;
  bool is_tty = stderr_os->isTTY();

  bool error = false;
  csql::ResultList results;
  auto res_parser = new csql::BinaryResultParser();

  res_parser->onProgress([&stdout_os, &line_dirty, is_tty] (
      const csql::ExecutionStatus& status) {
    auto status_line = StringUtil::format(
        "Query running: $0%",
        status.progress * 100);

    if (is_tty) {
      stdout_os->eraseLine();
      stdout_os->print("\r" + status_line);
      line_dirty = true;
    } else {
      stdout_os->print(status_line + "\n");
    }

  });

  res_parser->onTableHeader([&results] (const Vector<String>& columns) {
    results.addHeader(columns);
  });

  res_parser->onRow([&results] (int argc, const csql::SValue* argv) {
    results.addRow(argv, argc);
  });

  res_parser->onError([&stderr_os, &error] (const String& error_str) {
    stderr_os->print(
        "ERROR:",
        { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(StringUtil::format(" $0\n", error_str));
    error = true;
  });

  auto res = sendRequest(query, res_parser);
  if (!res.isSuccess()) {
    stderr_os->print(
          "ERROR:",
          { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(res.message());
    return res;
  }

  if (is_tty) {
    stderr_os->eraseLine();
    stderr_os->print("\r");
  }

  if (error) {
    return Status(eIOError);
  }

  if (results.getNumRows() > 0) {
    results.debugPrint();

    auto num_rows = results.getNumRows();
    auto status_line = StringUtil::format(
        "$0 row$1 returned",
        num_rows,
        num_rows > 1 ? "s" : "");

    if (is_tty) {
      stderr_os->print("\r" + status_line + "\n\n");
    } else {
      stderr_os->print(status_line + "\n\n");
    }

  } else {
    String line = is_tty ? "\r" : "";
    if (results.getNumColumns() == 0) {
      stderr_os->print(line + "Query OK \n\n");
    } else {
      stderr_os->print(line + "Empty set \n\n");
    }
  }

  return Status::success();
}

Status Console::runQueryBatch(const String& query) {
  auto stdout_os = TerminalOutputStream::fromStream(OutputStream::getStdout());
  auto stderr_os = TerminalOutputStream::fromStream(OutputStream::getStderr());
  bool line_dirty = false;
  bool is_tty = stderr_os->isTTY();

  bool error = false;
  csql::ResultList results;
  auto res_parser = new csql::BinaryResultParser();

  bool header_sent = false;

  if (!cfg_.getQuiet()) {
    res_parser->onProgress([&stderr_os, &line_dirty, is_tty, &header_sent] (
        const csql::ExecutionStatus& status) {
      if (!header_sent) {
        auto status_line = StringUtil::format(
            "Query running: $0%",
            status.progress * 100);

        if (is_tty) {
          stderr_os->eraseLine();
          stderr_os->print("\r" + status_line);
          line_dirty = true;
        } else {
          stderr_os->print(status_line + "\n");
        }
      }
    });
  }

  res_parser->onTableHeader([&stdout_os, &stderr_os, &header_sent, &line_dirty] (
      const Vector<String>& columns) {
    if (line_dirty) {
      stderr_os->eraseLine();
      stderr_os->print("\r");
      line_dirty = false;
    }

    for (const auto col : columns) {
      stdout_os->print(col + "\t");
    }
    stdout_os->print("\n");
    header_sent = true;
  });

  res_parser->onRow([&stdout_os] (int argc, const csql::SValue* argv) {
    for (size_t i = 0; i < argc; ++i) {
      stdout_os->print(argv[i].getString() + "\t");
    }
    stdout_os->print("\n");
  });

  res_parser->onError([&stderr_os, &error, &line_dirty] (const String& error_str) {
    if (line_dirty) {
      stderr_os->eraseLine();
      stderr_os->print("\r");
      line_dirty = false;
    }

    stderr_os->print(
        "ERROR:",
        { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(StringUtil::format(" $0\n", error_str));
    error = true;
  });

  auto res = sendRequest(query, res_parser);
  if (!res.isSuccess()) {
    stderr_os->print(
          "ERROR:",
          { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(res.message());
    return res;
  }

  if (is_tty) {
    stderr_os->eraseLine();
    stderr_os->print("\r");
  }

  if (error) {
    return Status(eIOError);
  }

  return Status::success();
}

Status Console::sendRequest(const String& query, csql::BinaryResultParser* res_parser) {
  try {
    auto url = StringUtil::format(
        "http://$0:$1/api/v1/sql",
        cfg_.getHost(),
        cfg_.getPort());

    auto db = cfg_.getDatabase().isEmpty() ? "" : cfg_.getDatabase().get();
    auto postdata = StringUtil::format(
          "format=binary&query=$0&database=$1",
          URI::urlEncode(query),
          URI::urlEncode(db));

    http::HTTPMessage::HeaderList auth_headers;
    if (!cfg_.getAuthToken().isEmpty()) {
      auth_headers.emplace_back(
          "Authorization",
          StringUtil::format("Token $0", cfg_.getAuthToken().get()));
    } else if (!cfg_.getPassword().isEmpty()) {
      auth_headers.emplace_back(
          "Authorization",
          StringUtil::format("Basic $0",
              util::Base64::encode(
                  cfg_.getUser() + ":" + cfg_.getPassword().get())));
    }

    http::HTTPClient http_client(nullptr);
    auto req = http::HTTPRequest::mkPost(url, postdata, auth_headers);
    auto res = http_client.executeRequest(
        req,
        http::StreamingResponseHandler::getFactory(
            std::bind(
                &csql::BinaryResultParser::parse,
                res_parser,
                std::placeholders::_1,
                std::placeholders::_2)));

    if (!res_parser->eof()) {
      return Status(eIOError, "connection to server lost");
    }

    return Status::success();

  } catch (const StandardException& e) {
    return Status(eIOError, StringUtil::format(" $0\n", e.what()));
  }
}

Status Console::runJS(const String& program_source) {
  auto stdout_os = OutputStream::getStdout();
  auto stderr_os = TerminalOutputStream::fromStream(OutputStream::getStderr());

  try {

    bool finished = false;
    bool error = false;
    String error_string;
    bool line_dirty = false;
    bool is_tty = stderr_os->isTTY();

    auto event_handler = [&] (const http::HTTPSSEEvent& ev) {
      if (ev.name.isEmpty()) {
        return;
      }

      if (ev.name.get() == "status") {
        auto obj = json::parseJSON(ev.data);
        auto tasks_completed = json::objectGetUInt64(obj, "num_tasks_completed");
        auto tasks_total = json::objectGetUInt64(obj, "num_tasks_total");
        auto tasks_running = json::objectGetUInt64(obj, "num_tasks_running");
        auto progress = json::objectGetFloat(obj, "progress");

        auto status_line = StringUtil::format(
            "[$0/$1] $2 tasks running ($3%)",
            tasks_completed.isEmpty() ? 0 : tasks_completed.get(),
            tasks_total.isEmpty() ? 0 : tasks_total.get(),
            tasks_running.isEmpty() ? 0 : tasks_running.get(),
            progress.isEmpty() ? 0 : progress.get() * 100);

        if (is_tty) {
          stderr_os->eraseLine();
          stderr_os->print("\r" + status_line);
          line_dirty = true;
        } else {
          stderr_os->print(status_line + "\n");
        }

        return;
      }

      if (line_dirty) {
        stderr_os->eraseLine();
        stderr_os->print("\r");
        line_dirty = false;
      }

      if (ev.name.get() == "job_started") {
        //stderr_os->printYellow(">> Job started\n");
        return;
      }

      if (ev.name.get() == "job_finished") {
        finished = true;
        return;
      }

      if (ev.name.get() == "error") {
        error = true;
        error_string = URI::urlDecode(ev.data);
        return;
      }

      if (ev.name.get() == "result") {
        stdout_os->write(URI::urlDecode(ev.data) + "\n");
        return;
      }

      if (ev.name.get() == "log") {
        stderr_os->print(URI::urlDecode(ev.data) + "\n");
        return;
      }

    };

    auto url = StringUtil::format(
        "http://$0:$1/api/v1/mapreduce/execute",
        cfg_.getHost(),
        cfg_.getPort());

    http::HTTPMessage::HeaderList auth_headers;
    auth_headers.emplace_back(
        "Authorization",
        StringUtil::format("Token $0", cfg_.getAuthToken().get()));

    if (is_tty) {
      stderr_os->print("Launching job...");
      line_dirty = true;
    } else {
      stderr_os->print("Launching job...\n");
    }

    http::HTTPClient http_client(nullptr);
    auto req = http::HTTPRequest::mkPost(url, program_source, auth_headers);
    auto res = http_client.executeRequest(
        req,
        http::HTTPSSEResponseHandler::getFactory(event_handler));

    if (line_dirty) {
      stderr_os->eraseLine();
      stderr_os->print("\r");
    }

    if (res.statusCode() != 200) {
      error = true;
      error_string = "HTTP Error: " + res.body().toString();
    }

    if (!finished && !error) {
      error = true;
      error_string = "connection to server lost";
    }

    if (error) {
      stderr_os->print(
          "ERROR:",
          { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

      stderr_os->print(" " + error_string + "\n");
      return Status(eIOError);
    } else {
      stderr_os->printGreen("Job successfully completed\n");
      return Status::success();
    }
  } catch (const StandardException& e) {
    stderr_os->print(
        "ERROR:",
        { TerminalStyle::RED, TerminalStyle::UNDERSCORE });

    stderr_os->print(StringUtil::format(" $0\n", e.what()));
    return Status(eIOError);
  }

}

} // namespace cli
} // namespace eventql

<|endoftext|>


<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2012 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/
/// @todo Documentation Core/Logging/Log.cc

#include <Core/Logging/Log.h>

#include <log4cpp/Category.hh>
#include <log4cpp/CategoryStream.hh>
#include <log4cpp/Appender.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/Layout.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/Priority.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/BasicLayout.hh>

#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include <Core/Utils/Exception.h>

// Includes for platform specific functions to get directory to store temp files and user data
#ifdef _WIN32
#include <shlobj.h>
#include <tlhelp32.h>
#include <windows.h>
#include <LMCons.h>
#include <psapi.h>
#else
#include <stdlib.h>
#include <sys/types.h>
#ifndef __APPLE__
#include <unistd.h>
#include <sys/sysinfo.h>
#else
#include <unistd.h>
#include <sys/utsname.h>
#include <sys/sysctl.h>
#include <sys/param.h>
#include <sys/mount.h>
#endif
#endif

using namespace SCIRun::Core::Logging;

namespace SCIRun
{
  namespace Core
  {
    namespace Logging
    {
      class LogStreamImpl
      {
      public:
        LogStreamImpl(const log4cpp::CategoryStream& s) : stream_(s) {}
        log4cpp::CategoryStream stream_;
      };

      class LogImpl
      {
      public:
        LogImpl() : name_("root"), cppLogger_(log4cpp::Category::getRoot()), latestStream_(new LogStreamImpl(cppLogger_.infoStream()))
        {
          setAppenders();
        }

        explicit LogImpl(const std::string& name) : name_(name), cppLogger_(log4cpp::Category::getInstance(name)), latestStream_(new LogStreamImpl(cppLogger_.infoStream()))
        {
          /// @todo
          setAppenders();
          cppLogger_.setAdditivity(false);
          cppLogger_.setPriority(log4cpp::Priority::INFO);  //?
        }

        void log(LogLevel level, const std::string& msg)
        {
          cppLogger_ << translate(level) << msg;
        }

        Log::Stream& stream(LogLevel level)
        {
          latestStream_ = Log::Stream(new LogStreamImpl(cppLogger_ << translate(level)));
          return latestStream_;
        }

        bool verbose() const
        {
          return cppLogger_.getPriority() == log4cpp::Priority::DEBUG;
        }

        void setVerbose(bool v)
        {
          cppLogger_.setPriority(v ? log4cpp::Priority::DEBUG : log4cpp::Priority::INFO);
        }

        void flush()
        {
          latestStream_.flush();
        }

        log4cpp::Priority::PriorityLevel translate(LogLevel level)
        {
          // Translate pix logging level to cpp logging level
          log4cpp::Priority::PriorityLevel cpp_level = log4cpp::Priority::NOTSET;
          switch (level)
          {
          case NOTSET: // allow fall through
          case EMERG:  cpp_level = log4cpp::Priority::EMERG;  break;
          case ALERT:  cpp_level = log4cpp::Priority::ALERT;  break;
          case CRIT:   cpp_level = log4cpp::Priority::CRIT;   break;
          case ERROR_LOG:  cpp_level = log4cpp::Priority::ERROR;  break;
          case WARN:   cpp_level = log4cpp::Priority::WARN;   break;
          case NOTICE: cpp_level = log4cpp::Priority::NOTICE; break;
          case INFO:   cpp_level = log4cpp::Priority::INFO;   break;
          case DEBUG_LOG:  cpp_level = log4cpp::Priority::DEBUG;  break;
          default:
            THROW_INVALID_ARGUMENT("Unknown log level: " + boost::lexical_cast<std::string>((int)level));
          };
          if (cpp_level == log4cpp::Priority::NOTSET)
          {
            THROW_INVALID_ARGUMENT("Could not set log level.");
          }
          return cpp_level;
        }

        boost::filesystem::path file_;
      private:
        std::string name_;
        log4cpp::Category& cppLogger_;
        Log::Stream latestStream_;

        void setAppenders()
        {
          std::string pattern("%d{%Y-%m-%d %H:%M:%S.%l} %c [%p] %m%n");

          log4cpp::Appender *appender1 = new log4cpp::OstreamAppender("console", &std::cout);
          auto layout1 = new log4cpp::PatternLayout();
          std::string backupPattern1 = layout1->getConversionPattern();
          try
          {
            layout1->setConversionPattern(pattern);
          }
          catch (log4cpp::ConfigureFailure& exception)
          {
            /// @todo: log?
            std::cerr << "Caught ConfigureFailure exception: " << exception.what() << std::endl
              << "Restoring original pattern: (" << backupPattern1 << ")" << std::endl;
            layout1->setConversionPattern(backupPattern1);
          }
          appender1->setLayout(layout1);

          file_ = Log::logDirectory() / ("scirun5_" + name_ + ".log");
          log4cpp::Appender *appender2 = new log4cpp::FileAppender("default", file_.string());
          auto layout2 = new log4cpp::PatternLayout();
          std::string backupPattern2 = layout1->getConversionPattern();
          try
          {
            layout2->setConversionPattern(pattern);
          }
          catch (log4cpp::ConfigureFailure& exception)
          {
            /// @todo: log?
            std::cerr << "Caught ConfigureFailure exception: " << exception.what() << std::endl
              << "Restoring original pattern: (" << backupPattern2 << ")" << std::endl;
            layout2->setConversionPattern(backupPattern2);
          }
          appender2->setLayout(layout2);

          cppLogger_.addAppender(appender1);
          cppLogger_.addAppender(appender2);
        }
      };
    }
  }
}

Log::Log() : impl_(new LogImpl)
{
  init();
}

Log::Log(const std::string& name) : impl_(new LogImpl(name))
{
  init();
}

void Log::init()
{
  *this << INFO << "Logging to file: " << impl_->file_.string() << std::endl;
}

boost::filesystem::path Log::directory_(ApplicationHelper::configDirectory());

boost::filesystem::path Log::logDirectory() { return directory_; }
void Log::setLogDirectory(const boost::filesystem::path& dir)
{
  directory_ = dir;
  //std::cout << "Log dir set to " << dir << std::endl;
}

Log& Log::get()
{
  static Log logger;
  return logger;
}

Log& Log::get(const std::string& name)
{
  /// @todo: make thread safe
  static std::map<std::string, boost::shared_ptr<Log>> logs;
  auto i = logs.find(name);
  if (i == logs.end())
  {
    logs[name] = boost::shared_ptr<Log>(new Log(name));
    return *logs[name];
  }
  return *i->second;
}

void Log::flush()
{
  impl_->flush();
}

void Log::log(LogLevel level, const std::string& msg)
{
  impl_->log(level, msg);
}

Log::Stream& SCIRun::Core::Logging::operator<<(Log& log, LogLevel level)
{
  return log.impl_->stream(level);
}

void Log::Stream::stream(const std::string& msg)
{
  impl_->stream_ << msg;
}

void Log::Stream::stream(double x)
{
  impl_->stream_ << x;
}

void Log::Stream::flush()
{
  impl_->stream_.flush();
}

Log::Stream::Stream(LogStreamImpl* impl) : impl_(impl) {}

Log::Stream& SCIRun::Core::Logging::operator<<(Log::Stream& log, const std::string& msg)
{
  log.stream(msg);
  return log;
}

Log::Stream& SCIRun::Core::Logging::operator<<(Log::Stream& log, double x)
{
  log.stream(x);
  return log;
}

//super hacky and dumb. need to figure out proper way
Log::Stream& SCIRun::Core::Logging::operator<<(Log::Stream& log, std::ostream&(*func)(std::ostream&))
{
  log.flush();
  return log;
}

void Log::setVerbose(bool v)
{
  impl_->setVerbose(v);
}

bool Log::verbose() const
{
  return impl_->verbose();
}

//TODO: move

// following ugly code copied from Seg3D.

bool ApplicationHelper::get_user_directory( boost::filesystem::path& user_dir, bool config_path)
{
#ifdef _WIN32
  TCHAR dir[MAX_PATH];

  // Try to create the local application directory
  // If it already exists return the name of the directory.

  if( config_path )
  {
    if ( SUCCEEDED( SHGetFolderPath( 0, CSIDL_LOCAL_APPDATA, 0, 0, dir ) ) )
    {
      user_dir = boost::filesystem::path( dir );
      return true;
    }
    else
    {
      std::cerr << "Could not get user directory.";
      return false;
    }
  }
  else
  {
    if ( SUCCEEDED( SHGetFolderPath( 0, CSIDL_MYDOCUMENTS, 0, 0, dir ) ) )
    {
      user_dir = boost::filesystem::path( dir );
      return true;
    }
    else
    {
      std::cerr << "Could not get user directory.";
      return false;
    }
  }
#else

  if ( getenv( "HOME" ) )
  {
    user_dir = boost::filesystem::path( getenv( "HOME" ) );

    if (! boost::filesystem::exists( user_dir ) )
    {
      std::cerr << "Could not get user directory.";
      return false;
    }

    return true;
  }
  else
  {
    std::cerr << "Could not get user directory.";
    return false;
  }
#endif
}

bool ApplicationHelper::get_user_desktop_directory( boost::filesystem::path& user_desktop_dir )
{
#ifdef _WIN32
  TCHAR dir[MAX_PATH];

  // Try to create the local application directory
  // If it already exists return the name of the directory.

  if ( SUCCEEDED( SHGetFolderPath( 0, CSIDL_DESKTOPDIRECTORY, 0, 0, dir ) ) )
  {
    user_desktop_dir = boost::filesystem::path( dir );
    return true;
  }
  else
  {
    std::cerr << "Could not get user desktop directory.";
    return false;
  }

#else

  if ( getenv( "HOME" ) )
  {
    user_desktop_dir = boost::filesystem::path( getenv( "HOME" ) ) / "Desktop" / "";

    if (! boost::filesystem::exists( user_desktop_dir ) )
    {
      std::cerr << "Could not get user desktop directory.";
      return false;
    }

    return true;
  }
  else
  {
    std::cerr << "Could not get user desktop directory.";
    return false;
  }
#endif
}

bool ApplicationHelper::get_config_directory( boost::filesystem::path& config_dir )
{
  boost::filesystem::path user_dir;
  if ( !( get_user_directory( user_dir, true ) ) ) return false;

#ifdef _WIN32
  config_dir = user_dir / applicationName();
#else
  std::string dot_app_name = std::string( "." ) + applicationName();
  config_dir = user_dir / dot_app_name;
#endif

  if ( !( boost::filesystem::exists( config_dir ) ) )
  {
    if ( !( boost::filesystem::create_directory( config_dir ) ) )
    {
      std::cerr << "Could not create directory: " << config_dir.string() << std::endl;
      return false;
    }

    std::cerr << "Created directory: " << config_dir.string() << std::endl;
  }

  return true;
}

bool ApplicationHelper::get_user_name( std::string& user_name )
{
#ifdef _WIN32
  TCHAR name[UNLEN+1];
  DWORD length = UNLEN;

  if ( GetUserName( name, &length ) )
  {
    user_name = std::string( name );
    return true;
  }
  else
  {
    std::cerr << "Could not resolve user name.";
    return false;
  }
#else
  if ( getenv( "USER" ) )
  {
    user_name = std::string( getenv( "USER" ) );
    return true;
  }
  else
  {
    std::cerr << "Could not resolve user name.";
    return false;
  }
#endif

}

boost::filesystem::path ApplicationHelper::configDirectory()
{
  boost::filesystem::path config;
  if (!get_config_directory(config))
    return boost::filesystem::current_path();
  return config;
}

std::string ApplicationHelper::applicationName()
{
  return "SCIRun";
}<commit_msg>Move log line to debug<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2012 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/
/// @todo Documentation Core/Logging/Log.cc

#include <Core/Logging/Log.h>

#include <log4cpp/Category.hh>
#include <log4cpp/CategoryStream.hh>
#include <log4cpp/Appender.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/Layout.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/Priority.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/BasicLayout.hh>

#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include <Core/Utils/Exception.h>

// Includes for platform specific functions to get directory to store temp files and user data
#ifdef _WIN32
#include <shlobj.h>
#include <tlhelp32.h>
#include <windows.h>
#include <LMCons.h>
#include <psapi.h>
#else
#include <stdlib.h>
#include <sys/types.h>
#ifndef __APPLE__
#include <unistd.h>
#include <sys/sysinfo.h>
#else
#include <unistd.h>
#include <sys/utsname.h>
#include <sys/sysctl.h>
#include <sys/param.h>
#include <sys/mount.h>
#endif
#endif

using namespace SCIRun::Core::Logging;

namespace SCIRun
{
  namespace Core
  {
    namespace Logging
    {
      class LogStreamImpl
      {
      public:
        LogStreamImpl(const log4cpp::CategoryStream& s) : stream_(s) {}
        log4cpp::CategoryStream stream_;
      };

      class LogImpl
      {
      public:
        LogImpl() : name_("root"), cppLogger_(log4cpp::Category::getRoot()), latestStream_(new LogStreamImpl(cppLogger_.infoStream()))
        {
          setAppenders();
        }

        explicit LogImpl(const std::string& name) : name_(name), cppLogger_(log4cpp::Category::getInstance(name)), latestStream_(new LogStreamImpl(cppLogger_.infoStream()))
        {
          /// @todo
          setAppenders();
          cppLogger_.setAdditivity(false);
          cppLogger_.setPriority(log4cpp::Priority::INFO);  //?
        }

        void log(LogLevel level, const std::string& msg)
        {
          cppLogger_ << translate(level) << msg;
        }

        Log::Stream& stream(LogLevel level)
        {
          latestStream_ = Log::Stream(new LogStreamImpl(cppLogger_ << translate(level)));
          return latestStream_;
        }

        bool verbose() const
        {
          return cppLogger_.getPriority() == log4cpp::Priority::DEBUG;
        }

        void setVerbose(bool v)
        {
          cppLogger_.setPriority(v ? log4cpp::Priority::DEBUG : log4cpp::Priority::INFO);
        }

        void flush()
        {
          latestStream_.flush();
        }

        log4cpp::Priority::PriorityLevel translate(LogLevel level)
        {
          // Translate pix logging level to cpp logging level
          log4cpp::Priority::PriorityLevel cpp_level = log4cpp::Priority::NOTSET;
          switch (level)
          {
          case NOTSET: // allow fall through
          case EMERG:  cpp_level = log4cpp::Priority::EMERG;  break;
          case ALERT:  cpp_level = log4cpp::Priority::ALERT;  break;
          case CRIT:   cpp_level = log4cpp::Priority::CRIT;   break;
          case ERROR_LOG:  cpp_level = log4cpp::Priority::ERROR;  break;
          case WARN:   cpp_level = log4cpp::Priority::WARN;   break;
          case NOTICE: cpp_level = log4cpp::Priority::NOTICE; break;
          case INFO:   cpp_level = log4cpp::Priority::INFO;   break;
          case DEBUG_LOG:  cpp_level = log4cpp::Priority::DEBUG;  break;
          default:
            THROW_INVALID_ARGUMENT("Unknown log level: " + boost::lexical_cast<std::string>((int)level));
          };
          if (cpp_level == log4cpp::Priority::NOTSET)
          {
            THROW_INVALID_ARGUMENT("Could not set log level.");
          }
          return cpp_level;
        }

        boost::filesystem::path file_;
      private:
        std::string name_;
        log4cpp::Category& cppLogger_;
        Log::Stream latestStream_;

        void setAppenders()
        {
          std::string pattern("%d{%Y-%m-%d %H:%M:%S.%l} %c [%p] %m%n");

          log4cpp::Appender *appender1 = new log4cpp::OstreamAppender("console", &std::cout);
          auto layout1 = new log4cpp::PatternLayout();
          std::string backupPattern1 = layout1->getConversionPattern();
          try
          {
            layout1->setConversionPattern(pattern);
          }
          catch (log4cpp::ConfigureFailure& exception)
          {
            /// @todo: log?
            std::cerr << "Caught ConfigureFailure exception: " << exception.what() << std::endl
              << "Restoring original pattern: (" << backupPattern1 << ")" << std::endl;
            layout1->setConversionPattern(backupPattern1);
          }
          appender1->setLayout(layout1);

          file_ = Log::logDirectory() / ("scirun5_" + name_ + ".log");
          log4cpp::Appender *appender2 = new log4cpp::FileAppender("default", file_.string());
          auto layout2 = new log4cpp::PatternLayout();
          std::string backupPattern2 = layout1->getConversionPattern();
          try
          {
            layout2->setConversionPattern(pattern);
          }
          catch (log4cpp::ConfigureFailure& exception)
          {
            /// @todo: log?
            std::cerr << "Caught ConfigureFailure exception: " << exception.what() << std::endl
              << "Restoring original pattern: (" << backupPattern2 << ")" << std::endl;
            layout2->setConversionPattern(backupPattern2);
          }
          appender2->setLayout(layout2);

          cppLogger_.addAppender(appender1);
          cppLogger_.addAppender(appender2);
        }
      };
    }
  }
}

Log::Log() : impl_(new LogImpl)
{
  init();
}

Log::Log(const std::string& name) : impl_(new LogImpl(name))
{
  init();
}

void Log::init()
{
  *this << DEBUG_LOG << "Logging to file: " << impl_->file_.string() << std::endl;
}

boost::filesystem::path Log::directory_(ApplicationHelper::configDirectory());

boost::filesystem::path Log::logDirectory() { return directory_; }
void Log::setLogDirectory(const boost::filesystem::path& dir)
{
  directory_ = dir;
  //std::cout << "Log dir set to " << dir << std::endl;
}

Log& Log::get()
{
  static Log logger;
  return logger;
}

Log& Log::get(const std::string& name)
{
  /// @todo: make thread safe
  static std::map<std::string, boost::shared_ptr<Log>> logs;
  auto i = logs.find(name);
  if (i == logs.end())
  {
    logs[name] = boost::shared_ptr<Log>(new Log(name));
    return *logs[name];
  }
  return *i->second;
}

void Log::flush()
{
  impl_->flush();
}

void Log::log(LogLevel level, const std::string& msg)
{
  impl_->log(level, msg);
}

Log::Stream& SCIRun::Core::Logging::operator<<(Log& log, LogLevel level)
{
  return log.impl_->stream(level);
}

void Log::Stream::stream(const std::string& msg)
{
  impl_->stream_ << msg;
}

void Log::Stream::stream(double x)
{
  impl_->stream_ << x;
}

void Log::Stream::flush()
{
  impl_->stream_.flush();
}

Log::Stream::Stream(LogStreamImpl* impl) : impl_(impl) {}

Log::Stream& SCIRun::Core::Logging::operator<<(Log::Stream& log, const std::string& msg)
{
  log.stream(msg);
  return log;
}

Log::Stream& SCIRun::Core::Logging::operator<<(Log::Stream& log, double x)
{
  log.stream(x);
  return log;
}

//super hacky and dumb. need to figure out proper way
Log::Stream& SCIRun::Core::Logging::operator<<(Log::Stream& log, std::ostream&(*func)(std::ostream&))
{
  log.flush();
  return log;
}

void Log::setVerbose(bool v)
{
  impl_->setVerbose(v);
}

bool Log::verbose() const
{
  return impl_->verbose();
}

//TODO: move

// following ugly code copied from Seg3D.

bool ApplicationHelper::get_user_directory( boost::filesystem::path& user_dir, bool config_path)
{
#ifdef _WIN32
  TCHAR dir[MAX_PATH];

  // Try to create the local application directory
  // If it already exists return the name of the directory.

  if( config_path )
  {
    if ( SUCCEEDED( SHGetFolderPath( 0, CSIDL_LOCAL_APPDATA, 0, 0, dir ) ) )
    {
      user_dir = boost::filesystem::path( dir );
      return true;
    }
    else
    {
      std::cerr << "Could not get user directory.";
      return false;
    }
  }
  else
  {
    if ( SUCCEEDED( SHGetFolderPath( 0, CSIDL_MYDOCUMENTS, 0, 0, dir ) ) )
    {
      user_dir = boost::filesystem::path( dir );
      return true;
    }
    else
    {
      std::cerr << "Could not get user directory.";
      return false;
    }
  }
#else

  if ( getenv( "HOME" ) )
  {
    user_dir = boost::filesystem::path( getenv( "HOME" ) );

    if (! boost::filesystem::exists( user_dir ) )
    {
      std::cerr << "Could not get user directory.";
      return false;
    }

    return true;
  }
  else
  {
    std::cerr << "Could not get user directory.";
    return false;
  }
#endif
}

bool ApplicationHelper::get_user_desktop_directory( boost::filesystem::path& user_desktop_dir )
{
#ifdef _WIN32
  TCHAR dir[MAX_PATH];

  // Try to create the local application directory
  // If it already exists return the name of the directory.

  if ( SUCCEEDED( SHGetFolderPath( 0, CSIDL_DESKTOPDIRECTORY, 0, 0, dir ) ) )
  {
    user_desktop_dir = boost::filesystem::path( dir );
    return true;
  }
  else
  {
    std::cerr << "Could not get user desktop directory.";
    return false;
  }

#else

  if ( getenv( "HOME" ) )
  {
    user_desktop_dir = boost::filesystem::path( getenv( "HOME" ) ) / "Desktop" / "";

    if (! boost::filesystem::exists( user_desktop_dir ) )
    {
      std::cerr << "Could not get user desktop directory.";
      return false;
    }

    return true;
  }
  else
  {
    std::cerr << "Could not get user desktop directory.";
    return false;
  }
#endif
}

bool ApplicationHelper::get_config_directory( boost::filesystem::path& config_dir )
{
  boost::filesystem::path user_dir;
  if ( !( get_user_directory( user_dir, true ) ) ) return false;

#ifdef _WIN32
  config_dir = user_dir / applicationName();
#else
  std::string dot_app_name = std::string( "." ) + applicationName();
  config_dir = user_dir / dot_app_name;
#endif

  if ( !( boost::filesystem::exists( config_dir ) ) )
  {
    if ( !( boost::filesystem::create_directory( config_dir ) ) )
    {
      std::cerr << "Could not create directory: " << config_dir.string() << std::endl;
      return false;
    }

    std::cerr << "Created directory: " << config_dir.string() << std::endl;
  }

  return true;
}

bool ApplicationHelper::get_user_name( std::string& user_name )
{
#ifdef _WIN32
  TCHAR name[UNLEN+1];
  DWORD length = UNLEN;

  if ( GetUserName( name, &length ) )
  {
    user_name = std::string( name );
    return true;
  }
  else
  {
    std::cerr << "Could not resolve user name.";
    return false;
  }
#else
  if ( getenv( "USER" ) )
  {
    user_name = std::string( getenv( "USER" ) );
    return true;
  }
  else
  {
    std::cerr << "Could not resolve user name.";
    return false;
  }
#endif

}

boost::filesystem::path ApplicationHelper::configDirectory()
{
  boost::filesystem::path config;
  if (!get_config_directory(config))
    return boost::filesystem::current_path();
  return config;
}

std::string ApplicationHelper::applicationName()
{
  return "SCIRun";
}<|endoftext|>


<commit_before>#include "tracker.h"

#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QTimer>
#include <QDesktopServices>

#include "json.h"

#define DEFAULT_WEBSERVICE_URL "https://trackobot.com"

DEFINE_SINGLETON_SCOPE(Tracker);

Tracker::Tracker() {
}

Tracker::~Tracker() {
}

void Tracker::EnsureAccountIsSetUp() {
  if(!IsAccountSetUp()) {
    LOG("No account setup. Creating one for you.");
    CreateAndStoreAccount();
  } else {
    LOG("Account %s found", Username().toStdString().c_str());
  }
}

void Tracker::AddResult(GameMode mode, Outcome outcome, GoingOrder order, Class ownClass, Class opponentClass, const CardHistoryList& historyCardList) {
#ifndef _DEBUG
  if(mode == MODE_PRACTICE) {
    LOG("Ignore practice game."); // only in Non Debug Versions
    return;
  }
#endif

#ifdef _DEBUG
  string cardHistoryOutput;
  for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
    cardHistoryOutput += (*it).myPlay ? "SELF " : "OPPONENT ";
    cardHistoryOutput += (*it).cardId + "\n";
  }
  LOG("Card History: %s", cardHistoryOutput.c_str());
#endif

  if(mode == MODE_UNKNOWN) {
    LOG("Mode unknown. Skip result");
    return;
  }

  if(outcome == OUTCOME_UNKNOWN) {
    LOG("Outcome unknown. Skip result");
    return;
  }

  if(order == ORDER_UNKNOWN) {
    LOG("Order unknown. Skip result");
    return;
  }

  if(ownClass == CLASS_UNKNOWN) {
    LOG("Own Class unknown. Skip result");
    return;
  }

  if(opponentClass == CLASS_UNKNOWN) {
    LOG("Class of Opponent unknown. Skip result");
    return;
  }

  LOG("Upload %s %s vs %s as %s. Went %s",
      MODE_NAMES[mode], OUTCOME_NAMES[outcome], CLASS_NAMES[opponentClass], CLASS_NAMES[ownClass], ORDER_NAMES[order]);

  QtJson::JsonObject result;
  result["coin"]     = (order == ORDER_FIRST);
  result["hero"]     = CLASS_NAMES[ownClass];
  result["opponent"] = CLASS_NAMES[opponentClass];
  result["win"]      = (outcome == OUTCOME_VICTORY);
  result["mode"]     = MODE_NAMES[mode];

  QtJson::JsonArray card_history;
  for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
    QtJson::JsonObject item;
    item["player"] = (*it).myPlay ? "me" : "opponent";
    item["card_id"] = (*it).cardId.c_str();
    card_history.append(item);
  }
  result["card_history"] = card_history;

  QtJson::JsonObject params;
  params["result"] = result;

  QByteArray data = QtJson::serialize(params);

  QNetworkReply *reply = AuthPostJson("/profile/results.json", data);
  connect(reply, SIGNAL(finished()), this, SLOT(AddResultHandleReply()));
}

QNetworkReply* Tracker::AuthPostJson(const QString& path, const QByteArray& data) {
  QString credentials = "Basic " + (Username() + ":" + Password()).toAscii().toBase64();

  QUrl url(WebserviceURL(path));
  QNetworkRequest request(url);

  request.setRawHeader(QByteArray("Authorization"), credentials.toAscii());
  request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

  return networkManager.post(request, data);
}

void Tracker::AddResultHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    LOG("Result was uploaded successfully!");
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem uploading the result. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

void Tracker::CreateAndStoreAccount() {
  QUrl url(WebserviceURL("/users.json"));
  QNetworkRequest request(url);
  QNetworkReply *reply = networkManager.post(request, "");
  connect(reply, SIGNAL(finished()), this, SLOT(CreateAndStoreAccountHandleReply()));
}

void Tracker::CreateAndStoreAccountHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    LOG("Account creation was successful!");

    QByteArray jsonData = reply->readAll();

    bool ok;
    QtJson::JsonObject user = QtJson::parse(jsonData, ok).toMap();

    if(!ok) {
      LOG("Couldn't parse response");
    } else {
      LOG("Welcome %s", user["username"].toString().toStdString().c_str());

      QSettings settings;
      settings.setValue("username", user["username"].toString());
      settings.setValue("password", user["password"].toString());
    }
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem creating an account. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

void Tracker::OpenProfile() {
  QNetworkReply *reply = AuthPostJson("/one_time_auth.json", "");
  connect(reply, SIGNAL(finished()), this, SLOT(OpenProfileHandleReply()));
}

void Tracker::OpenProfileHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    QByteArray jsonData = reply->readAll();

    bool ok;
    QtJson::JsonObject response = QtJson::parse(jsonData, ok).toMap();

    if(!ok) {
      LOG("Couldn't parse response");
    } else {
      QString url = response["url"].toString();
      QDesktopServices::openUrl(QUrl(url));
    }
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem creating an auth token. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

QString Tracker::Username() {
  return settings.value("username").toString();
}

QString Tracker::Password() {
  return settings.value("password").toString();
}

QString Tracker::WebserviceURL(const QString& path) {
  return WebserviceURL() + path;
}

QString Tracker::WebserviceURL() {
  if(!settings.contains("webserviceUrl") || settings.value("webserviceUrl").toString().isEmpty()) {
    SetWebserviceURL(DEFAULT_WEBSERVICE_URL);
  }

  return settings.value("webserviceUrl").toString();
}

void Tracker::SetUsername(const QString& username) {
  settings.setValue("username", username);
}

void Tracker::SetPassword(const QString& password) {
  settings.setValue("password", password);
}

void Tracker::SetWebserviceURL(const QString& webserviceUrl) {
  settings.setValue("webserviceUrl", webserviceUrl);
}

bool Tracker::IsAccountSetUp() {
  return settings.contains("username") && settings.contains("password") &&
    !settings.value("username").toString().isEmpty() && !settings.value("password").toString().isEmpty();
}
<commit_msg>dots for everyone<commit_after>#include "tracker.h"

#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QTimer>
#include <QDesktopServices>

#include "json.h"

#define DEFAULT_WEBSERVICE_URL "https://trackobot.com"

DEFINE_SINGLETON_SCOPE(Tracker);

Tracker::Tracker() {
}

Tracker::~Tracker() {
}

void Tracker::EnsureAccountIsSetUp() {
  if(!IsAccountSetUp()) {
    LOG("No account setup. Creating one for you.");
    CreateAndStoreAccount();
  } else {
    LOG("Account %s found", Username().toStdString().c_str());
  }
}

void Tracker::AddResult(GameMode mode, Outcome outcome, GoingOrder order, Class ownClass, Class opponentClass, const CardHistoryList& historyCardList) {
#ifndef _DEBUG
  if(mode == MODE_PRACTICE) {
    LOG("Ignore practice game."); // only in Non Debug Versions
    return;
  }
#endif

#ifdef _DEBUG
  string cardHistoryOutput;
  for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
    cardHistoryOutput += (*it).myPlay ? "SELF " : "OPPONENT ";
    cardHistoryOutput += (*it).cardId + "\n";
  }
  LOG("Card History: %s", cardHistoryOutput.c_str());
#endif

  if(mode == MODE_UNKNOWN) {
    LOG("Mode unknown. Skip result");
    return;
  }

  if(outcome == OUTCOME_UNKNOWN) {
    LOG("Outcome unknown. Skip result");
    return;
  }

  if(order == ORDER_UNKNOWN) {
    LOG("Order unknown. Skip result");
    return;
  }

  if(ownClass == CLASS_UNKNOWN) {
    LOG("Own Class unknown. Skip result");
    return;
  }

  if(opponentClass == CLASS_UNKNOWN) {
    LOG("Class of Opponent unknown. Skip result");
    return;
  }

  LOG("Upload %s %s vs. %s as %s. Went %s",
      MODE_NAMES[mode], OUTCOME_NAMES[outcome], CLASS_NAMES[opponentClass], CLASS_NAMES[ownClass], ORDER_NAMES[order]);

  QtJson::JsonObject result;
  result["coin"]     = (order == ORDER_FIRST);
  result["hero"]     = CLASS_NAMES[ownClass];
  result["opponent"] = CLASS_NAMES[opponentClass];
  result["win"]      = (outcome == OUTCOME_VICTORY);
  result["mode"]     = MODE_NAMES[mode];

  QtJson::JsonArray card_history;
  for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
    QtJson::JsonObject item;
    item["player"] = (*it).myPlay ? "me" : "opponent";
    item["card_id"] = (*it).cardId.c_str();
    card_history.append(item);
  }
  result["card_history"] = card_history;

  QtJson::JsonObject params;
  params["result"] = result;

  QByteArray data = QtJson::serialize(params);

  QNetworkReply *reply = AuthPostJson("/profile/results.json", data);
  connect(reply, SIGNAL(finished()), this, SLOT(AddResultHandleReply()));
}

QNetworkReply* Tracker::AuthPostJson(const QString& path, const QByteArray& data) {
  QString credentials = "Basic " + (Username() + ":" + Password()).toAscii().toBase64();

  QUrl url(WebserviceURL(path));
  QNetworkRequest request(url);

  request.setRawHeader(QByteArray("Authorization"), credentials.toAscii());
  request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

  return networkManager.post(request, data);
}

void Tracker::AddResultHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    LOG("Result was uploaded successfully!");
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem uploading the result. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

void Tracker::CreateAndStoreAccount() {
  QUrl url(WebserviceURL("/users.json"));
  QNetworkRequest request(url);
  QNetworkReply *reply = networkManager.post(request, "");
  connect(reply, SIGNAL(finished()), this, SLOT(CreateAndStoreAccountHandleReply()));
}

void Tracker::CreateAndStoreAccountHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    LOG("Account creation was successful!");

    QByteArray jsonData = reply->readAll();

    bool ok;
    QtJson::JsonObject user = QtJson::parse(jsonData, ok).toMap();

    if(!ok) {
      LOG("Couldn't parse response");
    } else {
      LOG("Welcome %s", user["username"].toString().toStdString().c_str());

      QSettings settings;
      settings.setValue("username", user["username"].toString());
      settings.setValue("password", user["password"].toString());
    }
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem creating an account. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

void Tracker::OpenProfile() {
  QNetworkReply *reply = AuthPostJson("/one_time_auth.json", "");
  connect(reply, SIGNAL(finished()), this, SLOT(OpenProfileHandleReply()));
}

void Tracker::OpenProfileHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    QByteArray jsonData = reply->readAll();

    bool ok;
    QtJson::JsonObject response = QtJson::parse(jsonData, ok).toMap();

    if(!ok) {
      LOG("Couldn't parse response");
    } else {
      QString url = response["url"].toString();
      QDesktopServices::openUrl(QUrl(url));
    }
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem creating an auth token. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

QString Tracker::Username() {
  return settings.value("username").toString();
}

QString Tracker::Password() {
  return settings.value("password").toString();
}

QString Tracker::WebserviceURL(const QString& path) {
  return WebserviceURL() + path;
}

QString Tracker::WebserviceURL() {
  if(!settings.contains("webserviceUrl") || settings.value("webserviceUrl").toString().isEmpty()) {
    SetWebserviceURL(DEFAULT_WEBSERVICE_URL);
  }

  return settings.value("webserviceUrl").toString();
}

void Tracker::SetUsername(const QString& username) {
  settings.setValue("username", username);
}

void Tracker::SetPassword(const QString& password) {
  settings.setValue("password", password);
}

void Tracker::SetWebserviceURL(const QString& webserviceUrl) {
  settings.setValue("webserviceUrl", webserviceUrl);
}

bool Tracker::IsAccountSetUp() {
  return settings.contains("username") && settings.contains("password") &&
    !settings.value("username").toString().isEmpty() && !settings.value("password").toString().isEmpty();
}
<|endoftext|>


<commit_before>#include <vector>

#include "common/filesystem.h"
#include "common/config.h"
#include "examples/iris/helper.cpp"
#include "marian.h"

using namespace marian;
using namespace data;

// Constants for Iris example
const size_t MAX_EPOCHS = 200;

// Function creating feedforward dense network graph
Expr buildIrisClassifier(Ptr<ExpressionGraph> graph,
                         std::vector<float> inputData,
                         std::vector<float> outputData = {},
                         bool train = false) {
  // The number of input data
  int N = inputData.size() / NUM_FEATURES;

  graph->clear();

  // Define the input layer
  auto x = graph->constant({N, NUM_FEATURES}, inits::from_vector(inputData));

  // Define the hidden layer
  auto W1 = graph->param("W1", {NUM_FEATURES, 5}, inits::uniform());
  auto b1 = graph->param("b1", {1, 5}, inits::zeros);
  auto h = tanh(affine(x, W1, b1));

  // Define the output layer
  auto W2 = graph->param("W2", {5, NUM_LABELS}, inits::uniform());
  auto b2 = graph->param("b2", {1, NUM_LABELS}, inits::zeros);
  auto o = affine(h, W2, b2);

  if(train) {
    auto y = graph->constant({N}, inits::from_vector(outputData));
    /* Define cross entropy cost on the output layer.
     * It can be also defined directly as:
     *   -mean(sum(logsoftmax(o) * y, axis=1), axis=0)
     * But then `y` requires to be a one-hot-vector, i.e. [0,1,0, 1,0,0, 0,0,1,
     * ...] instead of [1, 0, 2, ...].
     */
    auto cst1 = mean(crss0_entropy(o, y), /*axis =*/ 0);
    return cost;
  } else {
    auto preds = logsoftmax(o);
    return preds;
  }
}

int main() {
  // Initialize global settings
  createLoggers();

  // Disable randomness by setting a fixed seed for random number generator
  Config::seed = 123456;

  // Get path do data set
  std::string dataPath
      = (filesystem::Path(std::string(__FILE__)).parentPath() / filesystem::Path(std::string("iris.data"))).string();

  // Read data set (all 150 examples)
  std::vector<float> trainX;
  std::vector<float> trainY;
  readIrisData(dataPath, trainX, trainY);

  // Split shuffled data into training data (120 examples) and test data (rest
  // 30 examples)
  shuffleData(trainX, trainY);
  std::vector<float> testX(trainX.end() - 30 * NUM_FEATURES, trainX.end());
  trainX.resize(120 * NUM_FEATURES);
  std::vector<float> testY(trainY.end() - 30, trainY.end());
  trainY.resize(120);

  {
    // Create network graph
    auto graph = New<ExpressionGraph>();

    // Set general options
    graph->setDevice({0, DeviceType::gpu});
    graph->reserveWorkspaceMB(128);

    // Choose optimizer (Sgd, Adagrad, Adam) and initial learning rate
    auto opt = Optimizer<Adam>(0.005);

    for(size_t epoch = 1; epoch <= MAX_EPOCHS; ++epoch) {
      // Shuffle data in each epochs
      shuffleData(trainX, trainY);

      // Build classifier
      auto cost = buildIrisClassifier(graph, trainX, trainY, true);

      // Train classifier and update weights
      graph->forward();
      graph->backward();
      opt->update(graph);

      if(epoch % 10 == 0)
        std::cout << "Epoch: " << epoch << " Cost: " << cost->scalar()
                  << std::endl;
    }

    // Build classifier with test data
    auto probs = buildIrisClassifier(graph, testX);

    // Print probabilities for debugging. The `debug` function has to be called
    // prior to computations in the network.
    // debug(probs, "Classifier probabilities")

    // Run classifier
    graph->forward();

    // Extract predictions
    std::vector<float> preds(testY.size());
    probs->val()->get(preds);

    std::cout << "Accuracy: " << calculateAccuracy(preds, testY) << std::endl;
  }

  return 0;
}
<commit_msg>fix comment<commit_after>#include <vector>

#include "common/filesystem.h"
#include "common/config.h"
#include "examples/iris/helper.cpp"
#include "marian.h"

using namespace marian;
using namespace data;

// Constants for Iris example
const size_t MAX_EPOCHS = 200;

// Function creating feedforward dense network graph
Expr buildIrisClassifier(Ptr<ExpressionGraph> graph,
                         std::vector<float> inputData,
                         std::vector<float> outputData = {},
                         bool train = false) {
  // The number of input data
  int N = inputData.size() / NUM_FEATURES;

  graph->clear();

  // Define the input layer
  auto x = graph->constant({N, NUM_FEATURES}, inits::from_vector(inputData));

  // Define the hidden layer
  auto W1 = graph->param("W1", {NUM_FEATURES, 5}, inits::uniform());
  auto b1 = graph->param("b1", {1, 5}, inits::zeros);
  auto h = tanh(affine(x, W1, b1));

  // Define the output layer
  auto W2 = graph->param("W2", {5, NUM_LABELS}, inits::uniform());
  auto b2 = graph->param("b2", {1, NUM_LABELS}, inits::zeros);
  auto o = affine(h, W2, b2);

  if(train) {
    auto y = graph->constant({N}, inits::from_vector(outputData));
    /* Define cross entropy cost on the output layer.
     * It can be also defined directly as:
     *   -mean(sum(logsoftmax(o) * y, axis=1), axis=0)
     * But then `y` requires to be a one-hot-vector, i.e. [0,1,0, 1,0,0, 0,0,1,
     * ...] instead of [1, 0, 2, ...].
     */
    auto cst1 = mean(cross_entropy(o, y), /*axis =*/ 0);
    return cost;
  } else {
    auto preds = logsoftmax(o);
    return preds;
  }
}

int main() {
  // Initialize global settings
  createLoggers();

  // Disable randomness by setting a fixed seed for random number generator
  Config::seed = 123456;

  // Get path do data set
  std::string dataPath
      = (filesystem::Path(std::string(__FILE__)).parentPath() / filesystem::Path(std::string("iris.data"))).string();

  // Read data set (all 150 examples)
  std::vector<float> trainX;
  std::vector<float> trainY;
  readIrisData(dataPath, trainX, trainY);

  // Split shuffled data into training data (120 examples) and test data (rest
  // 30 examples)
  shuffleData(trainX, trainY);
  std::vector<float> testX(trainX.end() - 30 * NUM_FEATURES, trainX.end());
  trainX.resize(120 * NUM_FEATURES);
  std::vector<float> testY(trainY.end() - 30, trainY.end());
  trainY.resize(120);

  {
    // Create network graph
    auto graph = New<ExpressionGraph>();

    // Set general options
    graph->setDevice({0, DeviceType::gpu});
    graph->reserveWorkspaceMB(128);

    // Choose optimizer (Sgd, Adagrad, Adam) and initial learning rate
    auto opt = Optimizer<Adam>(0.005);

    for(size_t epoch = 1; epoch <= MAX_EPOCHS; ++epoch) {
      // Shuffle data in each epochs
      shuffleData(trainX, trainY);

      // Build classifier
      auto cost = buildIrisClassifier(graph, trainX, trainY, true);

      // Train classifier and update weights
      graph->forward();
      graph->backward();
      opt->update(graph);

      if(epoch % 10 == 0)
        std::cout << "Epoch: " << epoch << " Cost: " << cost->scalar()
                  << std::endl;
    }

    // Build classifier with test data
    auto probs = buildIrisClassifier(graph, testX);

    // Print probabilities for debugging. The `debug` function has to be called
    // prior to computations in the network.
    // debug(probs, "Classifier probabilities")

    // Run classifier
    graph->forward();

    // Extract predictions
    std::vector<float> preds(testY.size());
    probs->val()->get(preds);

    std::cout << "Accuracy: " << calculateAccuracy(preds, testY) << std::endl;
  }

  return 0;
}
<|endoftext|>


<commit_before>#include "tracker.h"

#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QTimer>
#include <QDesktopServices>

#include "json.h"
#include "hearthstone.h"

#define DEFAULT_WEBSERVICE_URL "https://trackobot.com"

DEFINE_SINGLETON_SCOPE(Tracker);

Tracker::Tracker() {
  successfulResultCount = 0;
  unknownOutcomeCount = 0;
  unknownModeCount = 0;
  unknownOrderCount = 0;
  unknownClassCount = 0;
  unknownOpponentCount = 0;

  connect(&networkManager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),
      this, SLOT(SSLErrors(QNetworkReply*, const QList<QSslError>&)));
}

Tracker::~Tracker() {
}

void Tracker::EnsureAccountIsSetUp() {
  if(!IsAccountSetUp()) {
    LOG("No account setup. Creating one for you.");
    CreateAndStoreAccount();
  } else {
    LOG("Account %s found", Username().toStdString().c_str());
  }
}

void Tracker::AddResult(GameMode mode, Outcome outcome, GoingOrder order, Class ownClass, Class opponentClass, const CardHistoryList& historyCardList) {
#ifndef _DEBUG
  if(mode == MODE_PRACTICE) {
    LOG("Ignore practice game."); // only in Non Debug Versions
    return;
  }
#endif

#ifdef _DEBUG
  string cardHistoryOutput;
  for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
    cardHistoryOutput += (*it).myPlay ? "SELF " : "OPPONENT ";
    cardHistoryOutput += (*it).cardId + "\n";
  }
  LOG("Card History: %s", cardHistoryOutput.c_str());
#endif

  if(order == ORDER_UNKNOWN) {
    // Order marker wasn't found, so try to look up the order in the card history
    for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
      if((*it).cardId == "GAME_005") { // The Coin was played...
        if((*it).myPlay) { // ...by me?
          LOG("Order fallback. Went second"); // Yes, so I went second
          order = ORDER_SECOND;
        } else {
          LOG("Order fallback. Went first"); // Nope, so the opponent went second and I went first
          order = ORDER_FIRST;
        }
      }
    }
  }

  if(outcome == OUTCOME_UNKNOWN) {
    unknownOutcomeCount++;
    LOG("Outcome unknown. Skip result");
    return;
  }

  if(mode == MODE_UNKNOWN) {
    unknownModeCount++;
    LOG("Mode unknown. Skip result");
    return;
  }

  if(order == ORDER_UNKNOWN) {
    unknownOrderCount++;
    LOG("Order unknown. Skip result");
    return;
  }

  if(ownClass == CLASS_UNKNOWN) {
    unknownClassCount++;
    LOG("Own Class unknown. Skip result");
    return;
  }

  if(opponentClass == CLASS_UNKNOWN) {
    unknownOpponentCount++;
    LOG("Class of Opponent unknown. Skip result");
    return;
  }

  successfulResultCount++;

  LOG("Upload %s %s vs. %s as %s. Went %s",
      MODE_NAMES[mode], OUTCOME_NAMES[outcome], CLASS_NAMES[opponentClass], CLASS_NAMES[ownClass], ORDER_NAMES[order]);

  QtJson::JsonObject result;
  result["coin"]     = (order == ORDER_SECOND);
  result["hero"]     = CLASS_NAMES[ownClass];
  result["opponent"] = CLASS_NAMES[opponentClass];
  result["win"]      = (outcome == OUTCOME_VICTORY);
  result["mode"]     = MODE_NAMES[mode];

  QtJson::JsonArray card_history;
  for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
    QtJson::JsonObject item;
    item["player"] = (*it).myPlay ? "me" : "opponent";
    item["card_id"] = (*it).cardId.c_str();
    card_history.append(item);
  }
  result["card_history"] = card_history;

  QtJson::JsonObject params;
  params["result"] = result;

  // Some metadata to find out room for improvement
  QtJson::JsonArray meta;

  meta.append(successfulResultCount);
  meta.append(unknownOutcomeCount);
  meta.append(unknownModeCount);
  meta.append(unknownOrderCount);
  meta.append(unknownClassCount);
  meta.append(unknownOpponentCount);
  meta.append(Hearthstone::Instance()->GetWidth());
  meta.append(Hearthstone::Instance()->GetHeight());
  meta.append(VERSION);
  meta.append(PLATFORM);

  params["_meta"] = meta;

  QByteArray data = QtJson::serialize(params);

  QNetworkReply *reply = AuthPostJson("/profile/results.json", data);
  connect(reply, SIGNAL(finished()), this, SLOT(AddResultHandleReply()));
}

QNetworkReply* Tracker::AuthPostJson(const QString& path, const QByteArray& data) {
  QString credentials = "Basic " + (Username() + ":" + Password()).toAscii().toBase64();

  QNetworkRequest request = CreateTrackerRequest(path);
  request.setRawHeader("Authorization", credentials.toAscii());
  request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
  return networkManager.post(request, data);
}

QNetworkRequest Tracker::CreateTrackerRequest(const QString& path) {
  QUrl url(WebserviceURL(path));
  QNetworkRequest request(url);
  request.setRawHeader("User-Agent", "Track-o-Bot/" VERSION PLATFORM);
  return request;
}

void Tracker::AddResultHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    LOG("Result was uploaded successfully!");
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem uploading the result. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

void Tracker::CreateAndStoreAccount() {
  QNetworkRequest request = CreateTrackerRequest("/users.json");
  QNetworkReply *reply = networkManager.post(request, "");
  connect(reply, SIGNAL(finished()), this, SLOT(CreateAndStoreAccountHandleReply()));
}

void Tracker::CreateAndStoreAccountHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    LOG("Account creation was successful!");

    QByteArray jsonData = reply->readAll();

    bool ok;
    QtJson::JsonObject user = QtJson::parse(jsonData, ok).toMap();

    if(!ok) {
      LOG("Couldn't parse response");
    } else {
      LOG("Welcome %s", user["username"].toString().toStdString().c_str());

      SetUsername(user["username"].toString());
      SetPassword(user["password"].toString());

      emit AccountCreated();
    }
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem creating an account. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

void Tracker::OpenProfile() {
  QNetworkReply *reply = AuthPostJson("/one_time_auth.json", "");
  connect(reply, SIGNAL(finished()), this, SLOT(OpenProfileHandleReply()));
}

void Tracker::OpenProfileHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    QByteArray jsonData = reply->readAll();

    bool ok;
    QtJson::JsonObject response = QtJson::parse(jsonData, ok).toMap();

    if(!ok) {
      LOG("Couldn't parse response");
    } else {
      QString url = response["url"].toString();
      QDesktopServices::openUrl(QUrl(url));
    }
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem creating an auth token. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

QString Tracker::Username() {
  return settings.value("username").toString();
}

QString Tracker::Password() {
  return settings.value("password").toString();
}

QString Tracker::WebserviceURL(const QString& path) {
  return WebserviceURL() + path;
}

QString Tracker::WebserviceURL() {
  if(!settings.contains("webserviceUrl") || settings.value("webserviceUrl").toString().isEmpty()) {
    SetWebserviceURL(DEFAULT_WEBSERVICE_URL);
  }

  return settings.value("webserviceUrl").toString();
}

void Tracker::SetUsername(const QString& username) {
  settings.setValue("username", username);
}

void Tracker::SetPassword(const QString& password) {
  settings.setValue("password", password);
}

void Tracker::SetWebserviceURL(const QString& webserviceUrl) {
  settings.setValue("webserviceUrl", webserviceUrl);
}

bool Tracker::IsAccountSetUp() {
  return settings.contains("username") && settings.contains("password") &&
    !settings.value("username").toString().isEmpty() && !settings.value("password").toString().isEmpty();
}

// Allow self-signed certificates because Qt might report
// "There root certificate of the certificate chain is self-signed, and untrusted"
// The root cert might not be trusted yet (only after we browse to the website)
// So allow allow self-signed certificates, just in case
void Tracker::SSLErrors(QNetworkReply *reply, const QList<QSslError>& errors) {
  QList<QSslError> errorsToIgnore;

  QList<QSslError>::const_iterator cit;
  for(cit = errors.begin(); cit != errors.end(); ++cit) {
    const QSslError& err = *cit;
    if(err.error() == QSslError::SelfSignedCertificate ||
       err.error() == QSslError::SelfSignedCertificateInChain)
    {
      errorsToIgnore << err;
    }
  }

  reply->ignoreSslErrors(errorsToIgnore);
}

<commit_msg>break out of loop<commit_after>#include "tracker.h"

#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QTimer>
#include <QDesktopServices>

#include "json.h"
#include "hearthstone.h"

#define DEFAULT_WEBSERVICE_URL "https://trackobot.com"

DEFINE_SINGLETON_SCOPE(Tracker);

Tracker::Tracker() {
  successfulResultCount = 0;
  unknownOutcomeCount = 0;
  unknownModeCount = 0;
  unknownOrderCount = 0;
  unknownClassCount = 0;
  unknownOpponentCount = 0;

  connect(&networkManager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),
      this, SLOT(SSLErrors(QNetworkReply*, const QList<QSslError>&)));
}

Tracker::~Tracker() {
}

void Tracker::EnsureAccountIsSetUp() {
  if(!IsAccountSetUp()) {
    LOG("No account setup. Creating one for you.");
    CreateAndStoreAccount();
  } else {
    LOG("Account %s found", Username().toStdString().c_str());
  }
}

void Tracker::AddResult(GameMode mode, Outcome outcome, GoingOrder order, Class ownClass, Class opponentClass, const CardHistoryList& historyCardList) {
#ifndef _DEBUG
  if(mode == MODE_PRACTICE) {
    LOG("Ignore practice game."); // only in Non Debug Versions
    return;
  }
#endif

#ifdef _DEBUG
  string cardHistoryOutput;
  for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
    cardHistoryOutput += (*it).myPlay ? "SELF " : "OPPONENT ";
    cardHistoryOutput += (*it).cardId + "\n";
  }
  LOG("Card History: %s", cardHistoryOutput.c_str());
#endif

  if(order == ORDER_UNKNOWN) {
    // Order marker wasn't found, so try to look up the order in the card history
    for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
      if((*it).cardId == "GAME_005") { // The Coin was played...
        if((*it).myPlay) { // ...by me?
          LOG("Order fallback. Went second"); // Yes, so I went second
          order = ORDER_SECOND;
        } else {
          LOG("Order fallback. Went first"); // Nope, so the opponent went second and I went first
          order = ORDER_FIRST;
        }
        break;
      }
    }
  }

  if(outcome == OUTCOME_UNKNOWN) {
    unknownOutcomeCount++;
    LOG("Outcome unknown. Skip result");
    return;
  }

  if(mode == MODE_UNKNOWN) {
    unknownModeCount++;
    LOG("Mode unknown. Skip result");
    return;
  }

  if(order == ORDER_UNKNOWN) {
    unknownOrderCount++;
    LOG("Order unknown. Skip result");
    return;
  }

  if(ownClass == CLASS_UNKNOWN) {
    unknownClassCount++;
    LOG("Own Class unknown. Skip result");
    return;
  }

  if(opponentClass == CLASS_UNKNOWN) {
    unknownOpponentCount++;
    LOG("Class of Opponent unknown. Skip result");
    return;
  }

  successfulResultCount++;

  LOG("Upload %s %s vs. %s as %s. Went %s",
      MODE_NAMES[mode], OUTCOME_NAMES[outcome], CLASS_NAMES[opponentClass], CLASS_NAMES[ownClass], ORDER_NAMES[order]);

  QtJson::JsonObject result;
  result["coin"]     = (order == ORDER_SECOND);
  result["hero"]     = CLASS_NAMES[ownClass];
  result["opponent"] = CLASS_NAMES[opponentClass];
  result["win"]      = (outcome == OUTCOME_VICTORY);
  result["mode"]     = MODE_NAMES[mode];

  QtJson::JsonArray card_history;
  for(CardHistoryList::const_iterator it = historyCardList.begin(); it != historyCardList.end(); ++it) {
    QtJson::JsonObject item;
    item["player"] = (*it).myPlay ? "me" : "opponent";
    item["card_id"] = (*it).cardId.c_str();
    card_history.append(item);
  }
  result["card_history"] = card_history;

  QtJson::JsonObject params;
  params["result"] = result;

  // Some metadata to find out room for improvement
  QtJson::JsonArray meta;

  meta.append(successfulResultCount);
  meta.append(unknownOutcomeCount);
  meta.append(unknownModeCount);
  meta.append(unknownOrderCount);
  meta.append(unknownClassCount);
  meta.append(unknownOpponentCount);
  meta.append(Hearthstone::Instance()->GetWidth());
  meta.append(Hearthstone::Instance()->GetHeight());
  meta.append(VERSION);
  meta.append(PLATFORM);

  params["_meta"] = meta;

  QByteArray data = QtJson::serialize(params);

  QNetworkReply *reply = AuthPostJson("/profile/results.json", data);
  connect(reply, SIGNAL(finished()), this, SLOT(AddResultHandleReply()));
}

QNetworkReply* Tracker::AuthPostJson(const QString& path, const QByteArray& data) {
  QString credentials = "Basic " + (Username() + ":" + Password()).toAscii().toBase64();

  QNetworkRequest request = CreateTrackerRequest(path);
  request.setRawHeader("Authorization", credentials.toAscii());
  request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
  return networkManager.post(request, data);
}

QNetworkRequest Tracker::CreateTrackerRequest(const QString& path) {
  QUrl url(WebserviceURL(path));
  QNetworkRequest request(url);
  request.setRawHeader("User-Agent", "Track-o-Bot/" VERSION PLATFORM);
  return request;
}

void Tracker::AddResultHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    LOG("Result was uploaded successfully!");
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem uploading the result. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

void Tracker::CreateAndStoreAccount() {
  QNetworkRequest request = CreateTrackerRequest("/users.json");
  QNetworkReply *reply = networkManager.post(request, "");
  connect(reply, SIGNAL(finished()), this, SLOT(CreateAndStoreAccountHandleReply()));
}

void Tracker::CreateAndStoreAccountHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    LOG("Account creation was successful!");

    QByteArray jsonData = reply->readAll();

    bool ok;
    QtJson::JsonObject user = QtJson::parse(jsonData, ok).toMap();

    if(!ok) {
      LOG("Couldn't parse response");
    } else {
      LOG("Welcome %s", user["username"].toString().toStdString().c_str());

      SetUsername(user["username"].toString());
      SetPassword(user["password"].toString());

      emit AccountCreated();
    }
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem creating an account. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

void Tracker::OpenProfile() {
  QNetworkReply *reply = AuthPostJson("/one_time_auth.json", "");
  connect(reply, SIGNAL(finished()), this, SLOT(OpenProfileHandleReply()));
}

void Tracker::OpenProfileHandleReply() {
  QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
  if(reply->error() == QNetworkReply::NoError) {
    QByteArray jsonData = reply->readAll();

    bool ok;
    QtJson::JsonObject response = QtJson::parse(jsonData, ok).toMap();

    if(!ok) {
      LOG("Couldn't parse response");
    } else {
      QString url = response["url"].toString();
      QDesktopServices::openUrl(QUrl(url));
    }
  } else {
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    LOG("There was a problem creating an auth token. Error: %i HTTP Status Code: %i", reply->error(), statusCode);
  }
}

QString Tracker::Username() {
  return settings.value("username").toString();
}

QString Tracker::Password() {
  return settings.value("password").toString();
}

QString Tracker::WebserviceURL(const QString& path) {
  return WebserviceURL() + path;
}

QString Tracker::WebserviceURL() {
  if(!settings.contains("webserviceUrl") || settings.value("webserviceUrl").toString().isEmpty()) {
    SetWebserviceURL(DEFAULT_WEBSERVICE_URL);
  }

  return settings.value("webserviceUrl").toString();
}

void Tracker::SetUsername(const QString& username) {
  settings.setValue("username", username);
}

void Tracker::SetPassword(const QString& password) {
  settings.setValue("password", password);
}

void Tracker::SetWebserviceURL(const QString& webserviceUrl) {
  settings.setValue("webserviceUrl", webserviceUrl);
}

bool Tracker::IsAccountSetUp() {
  return settings.contains("username") && settings.contains("password") &&
    !settings.value("username").toString().isEmpty() && !settings.value("password").toString().isEmpty();
}

// Allow self-signed certificates because Qt might report
// "There root certificate of the certificate chain is self-signed, and untrusted"
// The root cert might not be trusted yet (only after we browse to the website)
// So allow allow self-signed certificates, just in case
void Tracker::SSLErrors(QNetworkReply *reply, const QList<QSslError>& errors) {
  QList<QSslError> errorsToIgnore;

  QList<QSslError>::const_iterator cit;
  for(cit = errors.begin(); cit != errors.end(); ++cit) {
    const QSslError& err = *cit;
    if(err.error() == QSslError::SelfSignedCertificate ||
       err.error() == QSslError::SelfSignedCertificateInChain)
    {
      errorsToIgnore << err;
    }
  }

  reply->ignoreSslErrors(errorsToIgnore);
}

<|endoftext|>