diff --git a/cache.h b/cache.h index 73c9283..a4ef19d 100644 --- a/cache.h +++ b/cache.h @@ -85,6 +85,7 @@ public: std::string date_hidden; int age_when_found = -1; int age_now = -1; + int trail = 0; void set_date(const std::tm& t); void set_date_hidden(const std::tm& t); diff --git a/common.cpp b/common.cpp index 501bf29..8b85050 100644 --- a/common.cpp +++ b/common.cpp @@ -72,6 +72,18 @@ void show_histogram(const Caches& cc, std::string Cache::*ptr, const std::string show_histogram(histogram, caption, html, sort_by_val); } +void show_histogram(const pPowertrails& tt, const std::string& caption, bool html, bool sort_by_val) { + if (tt.empty()) return; + + std::map histogram; + + for (auto el : tt) { + histogram[el.second->name] = 100 * el.second->found / el.second->caches.size(); + } + + show_histogram(histogram, caption, html, sort_by_val); +} + void show_histogram(const std::map& data, const std::string& caption, bool html, bool sort_by_val) { if (data.empty()) return; diff --git a/common.h b/common.h index 8cdceb2..a1cf3a1 100644 --- a/common.h +++ b/common.h @@ -1,6 +1,7 @@ #pragma once #include "cache.h" +#include "powertrail.h" #include @@ -9,6 +10,7 @@ void htmlencode(std::string& data); void show_histogram(const Caches& cc, std::string Cache::*ptr, const std::string& caption, bool html = 0, bool sort_by_val = 1); void show_histogram(const pCaches& cc, std::string Cache::*ptr, const std::string& caption, bool html = 0, bool sort_by_val = 1); void show_histogram(const std::map& data, const std::string& caption, bool html = 0, bool sort_by_val = 1); +void show_histogram(const pPowertrails& tt, const std::string& caption, bool html = 0, bool sort_by_val = 1); void show_nested_histogram(const pCaches& fcc, std::string Cache::*ptr, std::string Cache::*ptr2, const std::string& caption, bool html = 0, bool sort_by_val = 1); int find_streak(const Date_Caches& cc, std::time_t& start); diff --git a/geostat.cpp b/geostat.cpp index e7da620..d517d77 100644 --- a/geostat.cpp +++ b/geostat.cpp @@ -6,6 +6,7 @@ #include "ocdb.h" #include "common.h" #include "region.h" +#include "powertrail.h" #include #include @@ -172,6 +173,10 @@ int main(int argc, char** argv) { } Caches cc; + PowertrailDB t; + Caches_in_Powertrails tc; + pPowertrails tt; + std::map region_count; if (get_not_found || get_owned) { @@ -198,6 +203,20 @@ int main(int argc, char** argv) { Okapi OCpl(ocpl_url, ocpl_key); if (!ocpl_user.empty()) ocpl_user_uuid = OCpl.get_uuid(ocpl_user); Caches tmp = OCpl.get_user_caches(ocpl_user_uuid, 0); + + t.read_from_json("powertrails.json"); + tc.read_from_json("caches_in_power.json"); + + for ( auto& c : tmp ) { + if (tc.data.count(c.code) > 0) { + c.trail = tc.data[c.code]; + t.data[c.trail].found++; + if (t.data[c.trail].found >= t.data[c.trail].caches.size() * t.data[c.trail].treshold_perc / 100) + t.data[c.trail].completed = 1; + tt[c.trail] = &(t.data[c.trail]); + } + } + std::copy(tmp.begin(), tmp.end(), std::back_inserter(cc)); ocpl_user_profile = OCpl.get_profile_url(ocpl_user_uuid); } @@ -884,6 +903,13 @@ int main(int argc, char** argv) { std::cout << "\n"; } } + + show_histogram(tt, "Power trails (completion percentage)", 1); + std::cout << "
\n"; + std::cout << "Number of completed power trails: " << std::count_if(tt.begin(), tt.end(), [&](const auto& a) { return a.second->completed; }) << "
\n"; + std::cout << "Number of started power trails: " << tt.size() << "
\n"; + std::cout << "
\n"; + } // end of main if if (get_not_found) { diff --git a/meson.build b/meson.build index d6a1925..43d5888 100644 --- a/meson.build +++ b/meson.build @@ -16,17 +16,19 @@ if not magick_dep.found() endif link = ['-lheatmap'] -src = ['geostat.cpp', 'okapi.cpp', 'cache.cpp', 'debug.cpp', 'heat.cpp', 'ocdb.cpp', 'common.cpp', 'region.cpp'] +src = ['geostat.cpp', 'okapi.cpp', 'cache.cpp', 'debug.cpp', 'heat.cpp', 'ocdb.cpp', 'common.cpp', 'region.cpp', 'powertrail.cpp'] src_fr = ['geofriends.cpp', 'okapi.cpp', 'cache.cpp', 'debug.cpp', 'common.cpp'] src_lst = ['geolist.cpp', 'okapi.cpp', 'cache.cpp', 'debug.cpp', 'common.cpp'] src_cli = ['geostat_cli.cpp', 'okapi.cpp', 'gpx.cpp', 'cache.cpp', 'debug.cpp', 'heat.cpp', 'ocdb.cpp', 'common.cpp', 'region.cpp'] src_db = ['geodb.cpp', 'debug.cpp', 'ocdb.cpp', 'okapi.cpp', 'cache.cpp', 'common.cpp'] +src_traildb = ['powertrail.cpp', 'debug.cpp', 'traildb.cpp'] executable('geostat', src, dependencies : [curl_dep, json_dep, magick_dep, sqlite_dep, boost_dep], link_args: link, install: true) executable('geofriends', src_fr, dependencies : [curl_dep, json_dep], install: true) executable('geolist', src_lst, dependencies : [curl_dep, json_dep], install: true) executable('geostat_cli', src_cli, dependencies : [curl_dep, json_dep, magick_dep, sqlite_dep, gpx_dep, boost_dep], link_args: link, install: true) executable('geodb', src_db, dependencies : [sqlite_dep, json_dep, curl_dep], install: true) +executable('traildb', src_traildb, dependencies : [sqlite_dep, curl_dep], install: false) configure_file(input: 'rating-star.png', output: '@PLAINNAME@', copy: true, install: true, install_dir: '.') configure_file(input: 'geo.css', output: '@PLAINNAME@', copy: true, install: true, install_dir: '.') diff --git a/powertrail.cpp b/powertrail.cpp new file mode 100644 index 0000000..a2ded84 --- /dev/null +++ b/powertrail.cpp @@ -0,0 +1,158 @@ +#include "powertrail.h" +#include "debug.h" + +#include +#include + +#include +#include +#include + +using json = nlohmann::json; + +static const std::regex regex_name("(.*?)"); +static const std::regex regex_date("(.*?)"); +static const std::regex regex_perc("(.*?)"); +static const std::regex regex_cache(""); + +void to_json(json& j, const Powertrail& item) { + j = json{ { "name", item.name }, { "date", item.date_str }, { "treshold", item.treshold_perc }, { "caches", item.caches } }; +} + +void from_json(const json& j, Powertrail& item) { + j.at("name").get_to(item.name); + j.at("date").get_to(item.date_str); + j.at("treshold").get_to(item.treshold_perc); + j.at("caches").get_to(item.caches); + strptime(item.date_str.c_str(), "%d-%m-%Y", &item.date); +} + + +PowertrailDB::PowertrailDB() { + curl = curl_easy_init(); + if (!curl) { + curl_global_cleanup(); + throw 0; + } + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&curl_output); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5); +} + +PowertrailDB::~PowertrailDB() { + curl_easy_cleanup(curl); +} + +void PowertrailDB::get_trail(int n) { + CURLcode curl_res; + std::smatch res; + + Powertrail T; + T.number = n; + + curl_output.clear(); + curl_easy_setopt(curl, CURLOPT_URL, (url + std::to_string(n)).c_str()); + curl_res = curl_easy_perform(curl); + if (curl_res != CURLE_OK) { + Debug(1) << "Connection failed: " << curl_easy_strerror(curl_res) << '\n'; + curl_easy_cleanup(curl); + std::exit(EXIT_FAILURE); + } + + if (std::regex_search(curl_output, res, regex_name) && res.size() == 2) { + T.name = res[1].str(); + if (T.name.empty()) return; + Debug(2) << "Read name: " << T.name; + } else return; + if (std::regex_search(curl_output, res, regex_date) && res.size() == 2) { + strptime(res[1].str().c_str(), "%d-%m-%Y", &T.date); + T.date_str = res[1].str(); + Debug(2) << "Read time: " << T.date_str; + } + if (std::regex_search(curl_output, res, regex_perc) && res.size() == 2) { + T.treshold_perc = std::stoi(res[1].str()); + Debug(2) << "Read percent: " << T.treshold_perc; + } + + curl_output.clear(); + curl_easy_setopt(curl, CURLOPT_URL, (url2 + std::to_string(n)).c_str()); + curl_res = curl_easy_perform(curl); + if (curl_res != CURLE_OK) { + Debug(1) << "Connection failed: " << curl_easy_strerror(curl_res) << '\n'; + curl_easy_cleanup(curl); + std::exit(EXIT_FAILURE); + } + + auto caches_begin = std::sregex_iterator(curl_output.begin(), curl_output.end(), regex_cache); + auto caches_end = std::sregex_iterator(); + + Debug(2) << "Found " << std::distance(caches_begin, caches_end) << " caches.\n"; + + for (std::sregex_iterator i = caches_begin; i!= caches_end; i++) { + std::smatch match = *i; + T.caches.insert(match[1].str()); +// Debug(2) << match[1].str(); + } + + data.insert({n, T}); +} + +// Callback for cURL easy interface used to save received output to std::string object +size_t PowertrailDB::write_cb(char* ptr, __attribute__((unused)) size_t size, size_t nmemb, void* userdata) { + std::string* str = reinterpret_cast(userdata); + str->append(ptr, nmemb); + return nmemb; +} + +void PowertrailDB::read_from_json(std::string file) { + std::ifstream datafile(file); + + try { + json j; + datafile >> j; + data = j.get>(); + } + catch (...) { + Debug(1) << "Cache file: " << file << " corrupted."; + std::exit(EXIT_FAILURE); + } +}; + +void PowertrailDB::save_to_json(std::string file) { + std::ofstream datafile(file); + json j(data); + datafile << j; +}; + +Caches_in_Powertrails::Caches_in_Powertrails(PowertrailDB db) { + for ( auto& t : db.data ) { + for ( auto& c : t.second.caches ) { + data.insert( {c, t.second.number} ); + } + } +} + +void Caches_in_Powertrails::read_from_json(std::string file) { + std::ifstream datafile(file); + + try { + json j; + datafile >> j; + data = j.get>(); +// data = j.get(); + } + catch (...) { + Debug(1) << "Cache file: " << file << " corrupted."; + std::exit(EXIT_FAILURE); + } +}; + +void Caches_in_Powertrails::save_to_json(std::string file) { + std::ofstream datafile(file); + json j(data); + datafile << j; +}; + +// void Caches_in_Powertrails::update_caches(Caches& cc) { +// }; diff --git a/powertrail.h b/powertrail.h new file mode 100644 index 0000000..8633eba --- /dev/null +++ b/powertrail.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include + +#include "cache.h" + +typedef void CURL; +class Caches_in_Powertrails; + +class Powertrail { +public: + int number; + std::string name; + std::tm date; + std::string date_str; + int treshold_perc; + std::unordered_set caches; + + int found = 0; + bool completed = 0; +}; + +class PowertrailDB { +public: + std::unordered_map data; + void get_trail(int n); + + CURL* curl; + mutable std::string curl_output; + + PowertrailDB(); + ~PowertrailDB(); + + void read_from_json(std::string file); + void save_to_json(std::string file); + +private: + inline const static std::string url = "https://opencaching.pl/powerTrail.php?ptAction=showSerie&ptrail="; + inline const static std::string url2 = "https://opencaching.pl/powerTrail/ajaxGetPowerTrailCaches.php?ptAction=showSerie&ptrail="; + + static size_t write_cb(char* ptr, size_t size, size_t nmemb, void* userdata); +}; + +class Caches_in_Powertrails { +public: + std::unordered_map data; + + Caches_in_Powertrails(PowertrailDB db); + Caches_in_Powertrails() {}; + + void read_from_json(std::string file); + void save_to_json(std::string file); +// void update_caches(Caches& cc); + +private: +}; + +typedef std::unordered_map pPowertrails; diff --git a/traildb.cpp b/traildb.cpp new file mode 100644 index 0000000..1efa52e --- /dev/null +++ b/traildb.cpp @@ -0,0 +1,25 @@ +#include "powertrail.h" +#include "debug.h" + +#include + +#include + +using json = nlohmann::json; + +// void to_json(json& j, const Powertrail& item) { +// j = json{ { "name", item.name }, { "date", item.date_str }, { "treshold", item.treshold_perc }, { "caches", item.caches } }; +// } + +int main() { + Debug::set_debug_level(5); + PowertrailDB T; + + for (int i = 1; i < 1675; i++) + T.get_trail(i); + + Caches_in_Powertrails cc(T); + + cc.save_to_json("caches_in_power.json"); + T.save_to_json("powertrails.json"); +}