geostat/powertrail.cpp

199 wiersze
5.8 KiB
C++

#include "powertrail.h"
#include "debug.h"
#include "common.h"
#include <regex>
#include <fstream>
#include <curl/curl.h>
#include <time.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
static const std::string url = "https://opencaching.pl/powerTrail.php?ptAction=showSerie&ptrail=";
static const std::string url2 = "https://opencaching.pl/powerTrail/ajaxGetPowerTrailCaches.php?ptAction=showSerie&ptrail=";
static const std::regex regex_name("<span id=\"powerTrailName\">(.*?)</span>");
static const std::regex regex_type("<span id=\"ptTypeName\">(.*?)</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.*?)\">.*?<img src=\"/images/log/(.*?)\\.png\"");
void to_json(json& j, const Powertrail& item) {
j = json{ { "name", item.name }, { "date", item.date_str }, { "treshold", item.treshold_perc }, { "active", item.active_caches }, { "caches", item.caches }, { "type", item.type } };
}
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("active").get_to(item.active_caches);
j.at("caches").get_to(item.caches);
j.at("type").get_to(item.type);
strptime(item.date_str.c_str(), "%d-%m-%Y", &item.date);
item.treshold = (item.caches.size() * item.treshold_perc + 99) / 100; // Integer division rounding up
item.active_caches_not_found = item.active_caches;
}
std::string Powertrail::link() const {
return "https://opencaching.pl/powerTrail.php?ptAction=showSerie&ptrail=" + std::to_string(number);
}
std::string Powertrail::link_name() const {
return "<a href=\"" + link() + "\">" + safe_name() + "</a>";
}
std::string Powertrail::safe_name() const {
std::string tmp = name;
htmlencode(tmp);
return tmp;
}
std::string Powertrail::get_type() const {
return types[type];
}
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(uint 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_type) && res.size() == 2) {
if (res[1].str() == "Geo-szkic")
T.type = geoszkic;
else if (res[1].str() == "Krajoznawcza")
T.type = krajoznawcza;
else if (res[1].str() == "Przyrodnicza")
T.type = przyrodnicza;
else if (res[1].str() == "Tematyczna")
T.type = tematyczna;
else
T.type = unknown_type;
}
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());
if (match[2].str() == "16x16-published")
T.active_caches++;
}
T.treshold = (T.caches.size() * T.treshold_perc + 99) / 100; // Integer division rounding up
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<uint, Powertrail>>();
}
catch (...) {
Debug(1) << "Cache file: " << file << " corrupted.";
std::exit(EXIT_FAILURE);
}
for (auto& p : data)
p.second.number = p.first;
};
void PowertrailDB::save_to_json(std::string file) {
std::ofstream datafile(file);
json j(data);
datafile << j;
};
Caches_in_Powertrails::Caches_in_Powertrails(const 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>>();
}
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;
};