kopia lustrzana https://gitlab.com/tomaszg/geostat
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
rodzic
fd8ceea907
commit
ffa05441c7
1
cache.h
1
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);
|
||||
|
|
12
common.cpp
12
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<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;
|
||||
|
||||
|
|
2
common.h
2
common.h
|
@ -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);
|
||||
|
|
26
geostat.cpp
26
geostat.cpp
|
@ -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) {
|
||||
|
|
|
@ -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: '.')
|
||||
|
|
|
@ -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) {
|
||||
// };
|
|
@ -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;
|
|
@ -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");
|
||||
}
|
Ładowanie…
Reference in New Issue