2019-09-30 00:34:56 +00:00
# include "ocdb.h"
# include "debug.h"
# include "okapi.h"
# include <string>
# include <fstream>
# include <map>
# include <sqlite3.h>
# include <nlohmann/json.hpp>
using json = nlohmann : : json ;
// using namespace std::literals::string_literals;
OCdb : : OCdb ( std : : string db_file ) {
int res ;
res = sqlite3_open ( db_file . c_str ( ) , & db ) ;
if ( res ! = SQLITE_OK ) {
Debug ( 1 ) < < sqlite3_errmsg ( db ) ;
throw 1 ;
}
if ( ! request ( " CREATE TABLE IF NOT EXISTS revision (revision INTEGER PRIMARY KEY); " ) | |
! request ( " CREATE TABLE IF NOT EXISTS caches (code TEXT PRIMARY KEY, name TEXT, location TEXT, type TEXT, status TEXT, size TEXT, difficulty REAL, terrain REAL, country TEXT, region TEXT, owner TEXT); " ) | |
! request ( " CREATE TABLE IF NOT EXISTS logs (uuid TEXT PRIMARY KEY, cache_code TEXT, date TEXT, user TEXT, type TEXT); " ) )
throw 1 ;
2019-09-30 23:48:49 +00:00
request ( " UPDATE revision SET revision = 0; " ) ;
read_revision ( ) ;
2019-09-30 00:34:56 +00:00
}
OCdb : : ~ OCdb ( ) {
request ( " UPDATE revision SET revision = " + std : : to_string ( revision ) + ' ; ' ) ;
sqlite3_close ( db ) ;
}
bool OCdb : : request ( std : : string req ) {
int res ;
res = sqlite3_prepare_v2 ( db , req . c_str ( ) , req . length ( ) + 1 , & stmt , NULL ) ;
if ( res ! = SQLITE_OK ) {
Debug ( 1 ) < < " Request \" " < < req < < " \" failed: \n " < < sqlite3_errmsg ( db ) ;
return 0 ;
}
res = sqlite3_step ( stmt ) ;
if ( res ! = SQLITE_DONE ) {
Debug ( 1 ) < < " Request \" " < < req < < " \" failed: \n " < < sqlite3_errmsg ( db ) ;
return 0 ;
}
sqlite3_finalize ( stmt ) ;
return 1 ;
}
bool OCdb : : init ( std : : string dump_path ) {
request ( " DELETE FROM caches; " ) ;
request ( " DELETE FROM logs; " ) ;
request ( " DELETE FROM revision; " ) ;
std : : ifstream file ( dump_path + " index.json " ) ;
json j ;
file > > j ;
revision = j [ " revision " ] ;
request ( " INSERT INTO revision VALUES ( " + std : : to_string ( revision ) + " ); " ) ;
for ( auto & el : j [ " data_files " ] . items ( ) ) {
init_part ( dump_path + el . value ( ) . get < std : : string > ( ) ) ;
}
return 1 ;
}
bool OCdb : : init_part ( std : : string json_file ) {
2019-10-01 15:11:38 +00:00
Debug ( 2 ) < < " Processing file: " < < json_file < < ' \n ' ;
2019-09-30 00:34:56 +00:00
std : : ifstream file ( json_file ) ;
json j ;
file > > j ;
2019-10-01 15:11:38 +00:00
Debug ( 5 ) < < " File: " < < json_file < < " read. \n " ;
2019-09-30 00:34:56 +00:00
for ( auto & el : j . items ( ) ) {
parse_item ( el . value ( ) ) ;
}
return 1 ;
}
bool OCdb : : update ( Okapi & oc ) {
2019-09-30 23:49:25 +00:00
Debug ( 2 ) < < " Fetching changelog since revision " < < revision < < " . \n " ;
std : : string output = oc . get_changelog_json ( revision ) ;
json j = json : : parse ( output ) ;
for ( auto & el : j [ " changelog " ] . items ( ) ) {
parse_item ( el . value ( ) ) ;
}
revision = j [ " revision " ] ;
if ( j [ " more " ] )
update ( oc ) ;
2019-09-30 00:34:56 +00:00
return 1 ;
}
bool OCdb : : parse_item ( json j ) {
if ( j . count ( " object_type " ) > 0 & & j [ " object_type " ] = = " geocache " ) {
if ( j . count ( " change_type " ) > 0 & & j [ " change_type " ] = = " replace " ) {
2019-10-01 15:11:38 +00:00
Debug ( 5 ) < < " Inserting/updating cache " < < j [ " object_key " ] [ " code " ] . get < std : : string > ( ) < < " . \n " ;
2019-09-30 00:34:56 +00:00
// if (j["object_key"]["code"] != j["data"]["code"]) {
// Debug(1) << "Code change " << j["object_key"]["code"] << " -> " << j["data"]["code"] <<'\n';
// }
update_cache ( j ) ;
}
else if ( j . count ( " change_type " ) > 0 & & j [ " change_type " ] = = " delete " ) {
2019-10-01 15:11:38 +00:00
Debug ( 2 ) < < " Deleting cache " < < j [ " object_key " ] [ " code " ] . get < std : : string > ( ) < < " . \n " ;
2019-09-30 00:34:56 +00:00
std : : string sql = " DELETE FROM caches WHERE code=' " + j [ " object_key " ] [ " code " ] . get < std : : string > ( ) + " '; " ;
request ( sql ) ;
}
else {
Debug ( 1 ) < < " Incorrect change type: " < < j [ " change_type " ] < < " . \n " ;
}
}
else if ( j . count ( " object_type " ) > 0 & & j [ " object_type " ] = = " log " ) {
if ( j [ " change_type " ] = = " replace " ) {
2019-10-01 15:11:38 +00:00
Debug ( 3 ) < < " Updating log " < < j [ " object_key " ] [ " uuid " ] < < " . \n " ;
2019-09-30 00:34:56 +00:00
update_log ( j ) ;
}
else if ( j . count ( " change_type " ) > 0 & & j [ " change_type " ] = = " delete " ) {
Debug ( 2 ) < < " Deleting log " < < j [ " object_key " ] [ " uuid " ] < < " . \n " ;
std : : string sql = " DELETE FROM logs WHERE uuid=' " + j [ " object_key " ] [ " uuid " ] . get < std : : string > ( ) + " '; " ;
request ( sql ) ;
}
else {
Debug ( 1 ) < < " Incorrect change type: " < < j [ " change_type " ] < < " . \n " ;
}
}
else {
Debug ( 1 ) < < " Incorrect object type: " < < j [ " object_type " ] < < " . \n " ;
}
return 1 ;
}
bool OCdb : : update_cache ( json j ) {
// (code TEXT PRIMARY KEY, name TEXT, location TEXT, type TEXT, status TEXT, size TEXT, difficulty REAL, terrain REAL, country TEXT, region TEXT, owner TEXT)
std : : map < std : : string , std : : string > fields ;
std : : map < std : : string , float > fields2 ;
int res ;
std : : string code = j [ " object_key " ] [ " code " ] . get < std : : string > ( ) ;
if ( j [ " data " ] [ " names " ] . count ( " pl " ) > 0 & & ! j [ " data " ] [ " names " ] [ " pl " ] . is_null ( ) )
fields [ " name " ] = j [ " data " ] [ " names " ] [ " pl " ] . get < std : : string > ( ) ;
else if ( j [ " data " ] [ " names " ] . count ( " en " ) > 0 & & ! j [ " data " ] [ " names " ] [ " en " ] . is_null ( ) )
fields [ " name " ] = j [ " data " ] [ " names " ] [ " en " ] . get < std : : string > ( ) ;
if ( j [ " data " ] . count ( " location " ) > 0 & & ! j [ " data " ] [ " location " ] . is_null ( ) )
fields [ " location " ] = j [ " data " ] [ " location " ] . get < std : : string > ( ) ;
if ( j [ " data " ] . count ( " type " ) > 0 & & ! j [ " data " ] [ " type " ] . is_null ( ) )
fields [ " type " ] = j [ " data " ] [ " type " ] . get < std : : string > ( ) ;
if ( j [ " data " ] . count ( " status " ) > 0 & & ! j [ " data " ] [ " status " ] . is_null ( ) )
fields [ " status " ] = j [ " data " ] [ " status " ] . get < std : : string > ( ) ;
if ( j [ " data " ] . count ( " size2 " ) > 0 & & ! j [ " data " ] [ " size2 " ] . is_null ( ) )
fields [ " size " ] = j [ " data " ] [ " size2 " ] . get < std : : string > ( ) ;
if ( j [ " data " ] . count ( " difficulty " ) > 0 & & ! j [ " data " ] [ " difficulty " ] . is_null ( ) )
fields2 [ " difficulty " ] = j [ " data " ] [ " difficulty " ] . get < float > ( ) ;
if ( j [ " data " ] . count ( " terrain " ) > 0 & & ! j [ " data " ] [ " terrain " ] . is_null ( ) )
fields2 [ " terrain " ] = j [ " data " ] [ " terrain " ] . get < float > ( ) ;
if ( j [ " data " ] . count ( " country " ) > 0 & & ! j [ " data " ] [ " country " ] . is_null ( ) )
fields [ " country " ] = j [ " data " ] [ " country " ] . get < std : : string > ( ) ;
if ( j [ " data " ] . count ( " region " ) > 0 & & ! j [ " data " ] [ " region " ] . is_null ( ) )
fields [ " region " ] = j [ " data " ] [ " region " ] . get < std : : string > ( ) ;
else if ( j [ " data " ] . count ( " state " ) > 0 & & ! j [ " data " ] [ " state " ] . is_null ( ) )
fields [ " region " ] = j [ " data " ] [ " state " ] . get < std : : string > ( ) ;
if ( j [ " data " ] [ " owner " ] . count ( " uuid " ) > 0 & & ! j [ " data " ] [ " owner " ] [ " uuid " ] . is_null ( ) )
fields [ " owner " ] = j [ " data " ] [ " owner " ] [ " uuid " ] . get < std : : string > ( ) ;
std : : string sql = " INSERT INTO caches (code, " ;
for ( auto & i : fields )
sql + = i . first + ' , ' ;
for ( auto & i : fields2 )
sql + = i . first + ' , ' ;
sql . pop_back ( ) ;
sql + = " ) VALUES (' " + code + " ', " ;
for ( auto & i : fields )
sql + = " ?, " ;
for ( auto & i : fields2 )
sql + = " ?, " ;
sql . pop_back ( ) ;
sql + = " ) ON CONFLICT(code) DO UPDATE SET " ;
for ( auto & i : fields )
sql + = i . first + " =excluded. " + i . first + ' , ' ;
for ( auto & i : fields2 )
sql + = i . first + " =excluded. " + i . first + ' , ' ;
sql . pop_back ( ) ;
sql + = ' ; ' ;
res = sqlite3_prepare_v2 ( db , sql . c_str ( ) , sql . length ( ) + 1 , & stmt , NULL ) ;
if ( res ! = SQLITE_OK ) {
Debug ( 1 ) < < " Request \" " < < sql < < " \" failed: \n " < < sqlite3_errmsg ( db ) ;
return 0 ;
}
int n = 1 ;
for ( auto & i : fields ) {
sqlite3_bind_text ( stmt , n + + , i . second . c_str ( ) , - 1 , nullptr ) ;
}
for ( auto & i : fields2 ) {
sqlite3_bind_double ( stmt , n + + , i . second ) ;
}
res = sqlite3_step ( stmt ) ;
if ( res ! = SQLITE_DONE ) {
Debug ( 1 ) < < " Request \" " < < sql < < " \" failed: \n " < < sqlite3_errmsg ( db ) ;
return 0 ;
}
sqlite3_finalize ( stmt ) ;
return 1 ;
}
bool OCdb : : update_log ( json j ) {
// logs (uuid TEXT PRIMARY KEY, cache_code TEXT, date TEXT, user TEXT, type TEXT);"))
std : : map < std : : string , std : : string > fields ;
int res ;
std : : string uuid = j [ " object_key " ] [ " uuid " ] . get < std : : string > ( ) ;
if ( j [ " data " ] . count ( " cache_code " ) > 0 & & ! j [ " data " ] [ " cache_code " ] . is_null ( ) )
fields [ " cache_code " ] = j [ " data " ] [ " cache_code " ] . get < std : : string > ( ) ;
if ( j [ " data " ] . count ( " date " ) > 0 & & ! j [ " data " ] [ " date " ] . is_null ( ) )
fields [ " date " ] = j [ " data " ] [ " date " ] . get < std : : string > ( ) ;
if ( j [ " data " ] [ " user " ] . count ( " uuid " ) > 0 & & ! j [ " data " ] [ " user " ] [ " uuid " ] . is_null ( ) )
fields [ " user " ] = j [ " data " ] [ " user " ] [ " uuid " ] . get < std : : string > ( ) ;
if ( j [ " data " ] . count ( " text " ) > 0 & & ! j [ " data " ] [ " text " ] . is_null ( ) )
fields [ " text " ] = j [ " data " ] [ " text " ] . get < std : : string > ( ) ;
std : : string sql = " INSERT INTO logs (uuid, " ;
for ( auto & i : fields )
sql + = i . first + ' , ' ;
sql . pop_back ( ) ;
sql + = " ) VALUES (' " + uuid + " ', " ;
for ( auto & i : fields )
sql + = " ?, " ;
sql . pop_back ( ) ;
sql + = " ) ON CONFLICT(uuid) DO UPDATE SET " ;
for ( auto & i : fields )
sql + = i . first + " =excluded. " + i . first + ' , ' ;
sql . pop_back ( ) ;
sql + = ' ; ' ;
res = sqlite3_prepare_v2 ( db , sql . c_str ( ) , sql . length ( ) + 1 , & stmt , NULL ) ;
if ( res ! = SQLITE_OK ) {
Debug ( 1 ) < < " Request \" " < < sql < < " \" failed: \n " < < sqlite3_errmsg ( db ) ;
return 0 ;
}
int n = 1 ;
for ( auto & i : fields ) {
sqlite3_bind_text ( stmt , n + + , i . second . c_str ( ) , - 1 , nullptr ) ;
}
res = sqlite3_step ( stmt ) ;
if ( res ! = SQLITE_DONE ) {
Debug ( 1 ) < < " Request \" " < < sql < < " \" failed: \n " < < sqlite3_errmsg ( db ) ;
return 0 ;
}
sqlite3_finalize ( stmt ) ;
return 1 ;
}
2019-09-30 23:48:49 +00:00
bool OCdb : : read_revision ( ) {
int res ;
std : : string sql = " SELECT revision FROM revision; " ;
res = sqlite3_prepare_v2 ( db , sql . c_str ( ) , sql . length ( ) + 1 , & stmt , NULL ) ;
if ( res ! = SQLITE_OK ) {
Debug ( 1 ) < < " Request \" " < < sql < < " \" failed: \n " < < sqlite3_errmsg ( db ) ;
return 0 ;
}
res = sqlite3_step ( stmt ) ;
if ( res ! = SQLITE_ROW ) {
Debug ( 1 ) < < " Request \" " < < sql < < " \" failed: \n " < < sqlite3_errmsg ( db ) ;
return 0 ;
}
revision = sqlite3_column_int ( stmt , 0 ) ;
sqlite3_finalize ( stmt ) ;
Debug ( 2 ) < < " Revision: " < < revision < < ' \n ' ;
return 1 ;
}