Rudimentary power trail support

Relies on pre-existing JSON databases generated by traildb. Note that completion of trail is calculated basing on current treshold and number of caches.
master
Tomasz Golinski 2022-07-12 12:54:56 +02:00
rodzic fd8ceea907
commit ffa05441c7
8 zmienionych plików z 288 dodań i 1 usunięć

Wyświetl plik

@ -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);

Wyświetl plik

@ -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<std::string, int> 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<std::string, int>& data, const std::string& caption, bool html, bool sort_by_val) {
if (data.empty()) return;

Wyświetl plik

@ -1,6 +1,7 @@
#pragma once
#include "cache.h"
#include "powertrail.h"
#include <functional>
@ -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<std::string, int>& 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);

Wyświetl plik

@ -6,6 +6,7 @@
#include "ocdb.h"
#include "common.h"
#include "region.h"
#include "powertrail.h"
#include <iostream>
#include <algorithm>
@ -172,6 +173,10 @@ int main(int argc, char** argv) {
}
Caches cc;
PowertrailDB t;
Caches_in_Powertrails tc;
pPowertrails tt;
std::map<std::string, int> 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 << "</div>\n";
}
}
show_histogram(tt, "Power trails (completion percentage)", 1);
std::cout << "<div class=\"basic_stats\">\n";
std::cout << "Number of completed power trails: <span class=\"value\">" << std::count_if(tt.begin(), tt.end(), [&](const auto& a) { return a.second->completed; }) << "</span><br>\n";
std::cout << "Number of started power trails: <span class=\"value\">" << tt.size() << "</span><br>\n";
std::cout << "</div>\n";
} // end of main if
if (get_not_found) {

Wyświetl plik

@ -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: '.')

158
powertrail.cpp 100644
Wyświetl plik

@ -0,0 +1,158 @@
#include "powertrail.h"
#include "debug.h"
#include <regex>
#include <fstream>
#include <curl/curl.h>
#include <time.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
static const std::regex regex_name("<span id=\"powerTrailName\">(.*?)</span>");
static const std::regex regex_date("<span id=\"powerTrailDateCreated\">(.*?)</span>");
static const std::regex regex_perc("<span id=\"powerTrailpercent\">(.*?)</span>");
static const std::regex regex_cache("<a href=\"(OP.*?)\">");
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<std::string*>(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<std::unordered_map<int, Powertrail>>();
}
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<std::unordered_map<std::string, int>>();
// data = j.get<Caches_in_Powertrails>();
}
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) {
// };

61
powertrail.h 100644
Wyświetl plik

@ -0,0 +1,61 @@
#pragma once
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <ctime>
#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<std::string> caches;
int found = 0;
bool completed = 0;
};
class PowertrailDB {
public:
std::unordered_map<int, Powertrail> 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<std::string, int> 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<int, const Powertrail*> pPowertrails;

25
traildb.cpp 100644
Wyświetl plik

@ -0,0 +1,25 @@
#include "powertrail.h"
#include "debug.h"
#include <fstream>
#include <nlohmann/json.hpp>
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");
}