Skip to content

Commit c6aef59

Browse files
Merge pull request #18 from Countly/sdk-608
[Sdk 608] Duration to events
2 parents e1649db + 7e87295 commit c6aef59

File tree

10 files changed

+184
-41
lines changed

10 files changed

+184
-41
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# for out of source builds
22
build/
3+
.idea/
4+
.vscode/
5+
.project
36

47
# cmake generates these files
58
src/countly.hpp

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
21.11.0
2+
* Fixed session duration issue.
3+
* Added functionality to report event duration manually.
4+
* 'startOnCloud' in 'Countly' is deprecated and this is going to be removed in the future.

CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ option(BUILD_SHARED_LIBS "Build shared libraries" ON)
1010
option(COUNTLY_USE_CUSTOM_HTTP "Use a custom HTTP library" OFF)
1111
option(COUNTLY_USE_SQLITE "Use SQLite" OFF)
1212
option(COUNTLY_BUILD_TESTS "Build test programs" OFF)
13+
option(COUNTLY_BUILD_SAMPLE "Build Sample programs" OFF)
1314

1415
if (NOT WIN32 AND NOT BUILD_SHARED_LIBS AND NOT COUNTLY_CUSTOM_HTTP)
1516
message(FATAL_ERROR "You must provide a custom HTTP function when compiling statically.")
@@ -72,3 +73,19 @@ if(COUNTLY_BUILD_TESTS)
7273
CXX_STANDARD_REQUIRED YES
7374
CXX_EXTENSIONS NO)
7475
endif()
76+
77+
if(COUNTLY_BUILD_SAMPLE)
78+
add_executable(countly-sample
79+
${CMAKE_CURRENT_SOURCE_DIR}/examples/example_integration.cpp)
80+
if(COUNTLY_USE_SQLITE)
81+
target_compile_definitions(countly-tests PRIVATE COUNTLY_USE_SQLITE)
82+
target_include_directories(countly-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/sqlite)
83+
endif()
84+
target_include_directories(countly-sample PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/doctest/doctest)
85+
target_include_directories(countly-sample PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/vendor/json/include)
86+
target_link_libraries(countly-sample countly)
87+
set_target_properties(countly-sample PROPERTIES
88+
CXX_STANDARD 11
89+
CXX_STANDARD_REQUIRED YES
90+
CXX_EXTENSIONS NO)
91+
endif()

examples/example_integration.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include <thread>
2+
#include <chrono>
3+
#include <random>
4+
#include <iostream>
5+
#include "countly.hpp"
6+
7+
using namespace std;
8+
9+
void printLog(Countly::LogLevel level, const string& msg) {
10+
string lvl = "[DEBUG]";
11+
switch (level) {
12+
case Countly::LogLevel::DEBUG:
13+
lvl = "[Debug]";
14+
break;
15+
case Countly::LogLevel::INFO:
16+
lvl = "[INFO]";
17+
break;
18+
case Countly::LogLevel::WARNING:
19+
lvl = "[WARNING]";
20+
break;
21+
case Countly::LogLevel::ERROR:
22+
lvl = "[ERROR]";
23+
break;
24+
case Countly::LogLevel::FATAL:
25+
lvl = "[FATAL]";
26+
break;
27+
28+
default:
29+
break;
30+
}
31+
32+
cout<<lvl<<msg<<endl;
33+
}
34+
35+
36+
37+
int main() {
38+
cout<<"Sample App"<<endl;
39+
Countly& ct = Countly::getInstance();
40+
ct.alwaysUsePost(true);
41+
ct.setDeviceID("test-device-id");
42+
43+
void (*logger_function)(Countly::LogLevel level, const std::string& message);
44+
logger_function = printLog;
45+
ct.setLogger(logger_function);
46+
// OS, OS_version, device, resolution, carrier, app_version);
47+
ct.SetMetrics("Windows 10", "10.22", "Mac", "800x600", "Carrier", "1.0");
48+
ct.setCustomUserDetails({{"Account Type", "Basic"}, {"Employer", "Company4"}});
49+
// Server and port
50+
ct.Start("YOUR_APP_KEY", "https://try.count.ly", 443);
51+
ct.SetMaxEventsPerMessage(10);
52+
ct.SetMinUpdatePeriod(10);
53+
ct.setUpdateInterval(15);
54+
55+
bool flag = true;
56+
while (flag) {
57+
cout<<"Choose your option:"<<endl;
58+
cout<<"1) Basic Event"<<endl;
59+
cout<<"2) Event with count and sum"<<endl;
60+
cout<<"3) Event with count, sum, duration"<<endl;
61+
cout<<"4) Event with sum, count, duration and segmentation"<<endl;
62+
cout<<"5) Update Session"<<endl;
63+
cout<<"0) Exit"<<endl;
64+
int a;
65+
cin>>a;
66+
switch (a) {
67+
case 1:
68+
ct.RecordEvent("Basic Event", 123);
69+
break;
70+
case 2:
71+
ct.RecordEvent("Event with count and sum", 644, 13.3);
72+
break;
73+
case 3: {
74+
Countly::Event event("Event with sum, count, duration", 1, 10, 60.5);
75+
ct.addEvent(event);
76+
break;
77+
}
78+
case 4: {
79+
std::map<std::string, std::string> segmentation;
80+
segmentation["height"] = "5.10";
81+
ct.RecordEvent("Event with sum, count, duration and segmentation", segmentation, 1, 0, 10);
82+
break;
83+
}
84+
case 5:
85+
ct.updateSession();
86+
break;
87+
case 0:
88+
flag = false;
89+
break;
90+
default:
91+
cout<<"Option not found!"<<endl;
92+
break;
93+
}
94+
}
95+
96+
ct.updateSession();
97+
ct.stop();
98+
99+
return 0;
100+
}

examples/using_thread.cpp

Lines changed: 0 additions & 37 deletions
This file was deleted.

include/countly.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ using json = nlohmann::json;
2121

2222
#define COUNTLY_SDK_NAME "cpp-native-unknown"
2323
#define COUNTLY_SDK_VERSION "0.1.0"
24-
#define COUNTLY_API_VERSION "19.8.0"
24+
#define COUNTLY_API_VERSION "21.11.0"
2525
#define COUNTLY_POST_THRESHOLD 2000
2626
#define COUNTLY_KEEPALIVE_INTERVAL 3000
2727
#define COUNTLY_MAX_EVENTS_DEFAULT 200
@@ -115,6 +115,7 @@ class Countly {
115115
public:
116116
Event(const std::string& key, size_t count = 1);
117117
Event(const std::string& key, size_t count, double sum);
118+
Event(const std::string& key, size_t count, double sum, double duration);
118119

119120
void setTimestamp();
120121

@@ -196,6 +197,16 @@ class Countly {
196197

197198
addEvent(event);
198199
}
200+
201+
void RecordEvent(const std::string key, std::map<std::string, std::string> segmentation, int count, double sum, double duration) {
202+
Event event(key, count, sum, duration);
203+
204+
for (auto key_value: segmentation) {
205+
event.addSegmentation(key_value.first, json::parse(key_value.second));
206+
}
207+
208+
addEvent(event);
209+
}
199210
private:
200211
void log(LogLevel level, const std::string& message);
201212

src/countly.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ void Countly::setDeviceID(const std::string& value, bool same_user) {
182182

183183
void Countly::start(const std::string& app_key, const std::string& host, int port, bool start_thread) {
184184
mutex.lock();
185+
log(Countly::LogLevel::INFO, "[Countly][start]");
185186
this->host = host;
186187
if (host.find("http://") == 0) {
187188
use_https = false;
@@ -219,7 +220,12 @@ void Countly::start(const std::string& app_key, const std::string& host, int por
219220
mutex.unlock();
220221
}
221222

223+
224+
/**
225+
* startOnCloud is deprecated and this is going to be removed in the future.
226+
*/
222227
void Countly::startOnCloud(const std::string& app_key) {
228+
log(Countly::LogLevel::WARNING, "[Countly][startOnCloud] 'startOnCloud' is deprecated, this is going to be removed in the future.");
223229
this->start(app_key, "https://cloud.count.ly", 443);
224230
}
225231

@@ -369,6 +375,7 @@ void Countly::flushEvents(std::chrono::seconds timeout) {
369375

370376
bool Countly::beginSession() {
371377
mutex.lock();
378+
log(Countly::LogLevel::INFO, "[Countly][beginSession]");
372379
if (began_session) {
373380
mutex.unlock();
374381
return true;
@@ -408,6 +415,8 @@ bool Countly::beginSession() {
408415
}
409416

410417
bool Countly::updateSession() {
418+
log(Countly::LogLevel::INFO, "[Countly][updateSession]");
419+
411420
mutex.lock();
412421
if (!began_session) {
413422
mutex.unlock();
@@ -486,6 +495,7 @@ bool Countly::updateSession() {
486495
mutex.unlock();
487496
return false;
488497
}
498+
489499
last_sent += duration;
490500
}
491501

@@ -505,7 +515,7 @@ bool Countly::updateSession() {
505515
return false;
506516
}
507517

508-
last_sent = Countly::getTimestamp();
518+
last_sent += duration;
509519

510520
#ifndef COUNTLY_USE_SQLITE
511521
event_queue.clear();
@@ -530,6 +540,7 @@ bool Countly::updateSession() {
530540
}
531541

532542
bool Countly::endSession() {
543+
log(Countly::LogLevel::INFO, "[Countly][endSession]");
533544
const std::chrono::system_clock::time_point now = Countly::getTimestamp();
534545
const auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch());
535546
const auto duration = std::chrono::duration_cast<std::chrono::seconds>(getSessionDuration(now));
@@ -626,6 +637,7 @@ static size_t countly_curl_write_callback(void *data, size_t byte_size, size_t n
626637
Countly::HTTPResponse Countly::sendHTTP(std::string path, std::string data) {
627638
bool use_post = always_use_post || (data.size() > COUNTLY_POST_THRESHOLD);
628639

640+
log(Countly::LogLevel::DEBUG, "[Countly][sendHTTP] data: "+ data);
629641
if (!salt.empty()) {
630642
unsigned char checksum[SHA256_DIGEST_LENGTH];
631643
std::string salted_data = data + salt;
@@ -762,6 +774,8 @@ Countly::HTTPResponse Countly::sendHTTP(std::string path, std::string data) {
762774
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
763775
}
764776

777+
log(Countly::LogLevel::DEBUG, "[Countly][sendHTTP] request: " + full_url_stream.str());
778+
765779
std::string full_url = full_url_stream.str();
766780
curl_easy_setopt(curl, CURLOPT_URL, full_url.c_str());
767781

@@ -771,24 +785,26 @@ Countly::HTTPResponse Countly::sendHTTP(std::string path, std::string data) {
771785

772786
curl_code = curl_easy_perform(curl);
773787
if (curl_code == CURLE_OK) {
788+
774789
long status_code;
775790
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);
776791
response.success = (status_code >= 200 && status_code < 300);
777792
if (!body.empty()) {
778793
response.data = json::parse(body);
779794
}
780795
}
781-
782796
curl_easy_cleanup(curl);
783797
}
784798
#endif
799+
log(Countly::LogLevel::DEBUG, "[Countly][sendHTTP] response: " + response.data.dump());
785800
return response;
786801
#endif
802+
787803
}
788804

789805
std::chrono::system_clock::duration Countly::getSessionDuration(std::chrono::system_clock::time_point now) {
790806
mutex.lock();
791-
std::chrono::system_clock::duration duration = last_sent - now;
807+
std::chrono::system_clock::duration duration = now - last_sent;
792808
mutex.unlock();
793809
return duration;
794810
}
@@ -798,6 +814,7 @@ std::chrono::system_clock::duration Countly::getSessionDuration() {
798814
}
799815

800816
void Countly::updateLoop() {
817+
log(Countly::LogLevel::DEBUG, "[Countly][updateLoop]");
801818
mutex.lock();
802819
running = true;
803820
mutex.unlock();

src/event.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ Countly::Event::Event(const std::string& key, size_t count, double sum) : object
1111
object["sum"] = sum;
1212
}
1313

14+
Countly::Event::Event(const std::string& key, size_t count, double sum, double duration) : object({}), timer_running(false) {
15+
object["key"] = key;
16+
object["count"] = count;
17+
object["sum"] = sum;
18+
object["dur"] = duration;
19+
}
20+
1421
void Countly::Event::setTimestamp() {
1522
timestamp = Countly::getTimestamp();
1623
object["timestamp"] = std::chrono::duration_cast<std::chrono::seconds>(timestamp.time_since_epoch()).count();

tests/event.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ TEST_CASE("events are serialized correctly") {
5353
CHECK(event.serialize() == "{\"count\":3,\"key\":\"lose\",\"segmentation\":{\"points\":2000}}");
5454
}
5555

56+
SUBCASE("with count, sum, duration and segmentation") {
57+
Countly::Event event("lose", 3, 10, 100);
58+
event.addSegmentation("points", 2000);
59+
CHECK(event.serialize() == "{\"count\":3,\"dur\":100.0,\"key\":\"lose\",\"segmentation\":{\"points\":2000},\"sum\":10.0}");
60+
}
61+
5662
SUBCASE("with multibyte strings") {
5763
Countly::Event event("测试", 1);
5864
event.addSegmentation("苹果", "美味");

tests/main.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,21 @@ TEST_CASE("events are sent correctly") {
198198
CHECK(http_call.data["events"] == "[{\"count\":2,\"key\":\"win\"},{\"count\":1,\"key\":\"achievement\"}]");
199199
}
200200

201+
SUBCASE("event with count, sum, duration and segmentation is sent") {
202+
Countly::Event event("lose", 3, 10, 100);
203+
event.addSegmentation("points", 2000);
204+
countly.addEvent(event);
205+
206+
WAIT_FOR_SQLITE(1);
207+
countly.updateSession();
208+
209+
HTTPCall http_call = popHTTPCall();
210+
CHECK(!http_call.use_post);
211+
CHECK(http_call.data["app_key"] == COUNTLY_TEST_APP_KEY);
212+
CHECK(http_call.data["device_id"] == COUNTLY_TEST_DEVICE_ID);
213+
CHECK(http_call.data["events"] == "[{\"count\":3,\"dur\":100.0,\"key\":\"lose\",\"segmentation\":{\"points\":2000},\"sum\":10.0}]");
214+
}
215+
201216
SUBCASE("100 events are sent") {
202217
for (int i = 0; i < 100; i++) {
203218
Countly::Event event("click", i);

0 commit comments

Comments
 (0)