Skip to content

Commit 093eff4

Browse files
committed
Some tests and benchmarks for places
1 parent d99d569 commit 093eff4

File tree

15 files changed

+481
-40
lines changed

15 files changed

+481
-40
lines changed

.github/workflows/worker.yaml

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,17 @@ jobs:
3030
cmake-version: '3.30.x'
3131
- name: CMake Version
3232
run: cmake --version
33-
- name: Install Dependencies
33+
- name: Run Lint
3434
run: |
35-
sudo apt install -y libspdlog-dev clang-tidy clang-format
3635
pip install cpplint
3736
cpplint --version
38-
clang-tidy --version
39-
clang-format --version
40-
- name: Run Lint
41-
run: make lint
37+
make lint
4238
- name: Run Build
4339
run: |
44-
git submodule update --init --recursive
45-
make build
40+
make build-with-tests
4641
- name: Run Test & Cover
4742
run: |
48-
make test-install
49-
make cover
43+
make test
5044
- name: Publish Coverage
5145
uses: codecov/codecov-action@v5
5246
with:

.gitmodules

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

protos/api.proto

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@ message MediaItemPlaceRequest {
4545
string id = 2;
4646
optional string postcode = 3;
4747
optional string country = 4;
48-
optional string state = 5;
49-
optional string city = 6;
50-
optional string town = 7;
48+
optional string locality = 5; // city, town, village, municipality
49+
optional string area = 6; // suburb, road, neighbourhood
5150
}
5251

5352
message MediaItemThingRequest {

worker/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ Thumbs.db
2222
.idea/
2323
fly.toml
2424
build/
25-
generated/
25+
generated/
26+
third_party/

worker/CMakeLists.txt

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
cmake_minimum_required(VERSION 3.30)
2+
3+
set(CMAKE_CXX_STANDARD 17)
4+
25
project(worker)
6+
7+
option(BUILD_TESTS "Build Tests and Benchmarks" OFF)
8+
39
if(NOT DEFINED VERSION)
410
set(VERSION "dev")
511
endif()
12+
613
execute_process(
714
COMMAND git rev-parse HEAD
815
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@@ -13,18 +20,65 @@ execute_process(
1320
if(NOT GIT_SHA)
1421
set(GIT_SHA "-")
1522
endif()
16-
add_subdirectory(third_party/spdlog)
23+
24+
include(cmake/Dependencies.cmake)
25+
26+
add_library(worker_components STATIC
27+
src/metadata.cc
28+
src/preview_thumbnail.cc
29+
src/places.cc)
30+
31+
target_include_directories(worker_components PRIVATE
32+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/simdjson/include
33+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/simdjson/src
34+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/cpr/include
35+
${CMAKE_CURRENT_SOURCE_DIR}/include)
36+
37+
target_link_libraries(worker_components PRIVATE
38+
spdlog::spdlog
39+
cpr::cpr
40+
simdjson::simdjson)
41+
42+
set_target_properties(worker_components PROPERTIES
43+
CXX_STANDARD 17
44+
CXX_EXTENSIONS OFF)
45+
1746
add_executable(worker
1847
src/main.cc
1948
src/metadata.cc
2049
src/preview_thumbnail.cc
2150
src/places.cc)
22-
target_include_directories(worker PRIVATE include)
51+
52+
target_include_directories(worker PRIVATE
53+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/simdjson/include
54+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/simdjson/src
55+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/cpr/include
56+
${CMAKE_CURRENT_SOURCE_DIR}/include)
57+
2358
target_compile_definitions(worker PRIVATE
2459
DEFAULT_VERSION="${VERSION}"
25-
DEFAULT_GIT_SHA="${GIT_SHA}"
26-
)
27-
target_link_libraries(worker PRIVATE spdlog::spdlog)
60+
DEFAULT_GIT_SHA="${GIT_SHA}")
61+
62+
target_link_libraries(worker PRIVATE
63+
spdlog::spdlog
64+
cpr::cpr
65+
simdjson::simdjson)
66+
2867
set_target_properties(worker PROPERTIES
2968
CXX_STANDARD 17
30-
CXX_EXTENSIONS OFF)
69+
CXX_EXTENSIONS OFF)
70+
71+
if(BUILD_TESTS)
72+
enable_testing()
73+
74+
set(BENCHMARK_ENABLE_TESTING OFF)
75+
set(GTEST_CREATE_TESTS OFF)
76+
set(INSTALL_GTEST OFF)
77+
78+
include(FetchContent)
79+
FetchContent_MakeAvailable(googletest benchmark)
80+
81+
enable_testing()
82+
83+
add_subdirectory(tests)
84+
endif()

worker/Makefile

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,23 @@ install:
2626
.PHONY: build
2727
build:
2828
@mkdir -p build
29-
@cmake -DCMAKE_PREFIX_PATH=${MY_INSTALL_DIR} . -B build
29+
@cmake -S . -B build -DCMAKE_PREFIX_PATH=${MY_INSTALL_DIR}
30+
@cd build && make -j 4
31+
32+
.PHONY: build-with-tests
33+
build-with-tests:
34+
@mkdir -p build
35+
@cmake -S . -B build -DCMAKE_PREFIX_PATH=${MY_INSTALL_DIR} -DBUILD_TESTS=ON
3036
@cd build && make -j 4
3137

3238
.PHONY: run
3339
run:
3440
@./build/worker
3541

36-
.PHONY: test-install
37-
test-install:
38-
@git submodule update --init --recursive
39-
4042
.PHONY: test
4143
test:
42-
@./build/worker_tests
43-
@./build/worker_benchmarks
44+
@./build/tests/worker_tests
45+
@./build/tests/worker_benchmarks
4446

4547
.PHONY: cover
4648
cover:
@@ -53,7 +55,6 @@ cover-html: cover
5355
.PHONY: lint
5456
lint:
5557
@cpplint --recursive include/ src/ tests/
56-
@clang-tidy src/*.cc -- -std=c++17 -I./include -I/usr/include -I/usr/local/include/ -I/opt/homebrew/include/
5758

5859
.PHONY: proto
5960
proto:

worker/cmake/Dependencies.cmake

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
include(FetchContent)
2+
set(FETCHCONTENT_BASE_DIR ${CMAKE_SOURCE_DIR}/third_party)
3+
function(fetch_dependency name repo tag)
4+
FetchContent_Declare(
5+
${name}
6+
GIT_REPOSITORY ${repo}
7+
GIT_TAG ${tag}
8+
)
9+
FetchContent_MakeAvailable(${name})
10+
endfunction()
11+
12+
fetch_dependency(cpr https://github.com/libcpr/cpr.git 1.11.2)
13+
fetch_dependency(simdjson https://github.com/simdjson/simdjson.git v3.12.3)
14+
fetch_dependency(spdlog https://github.com/gabime/spdlog.git v1.15.2)
15+
16+
if(BUILD_TESTS)
17+
fetch_dependency(googletest https://github.com/google/googletest.git v1.16.0)
18+
fetch_dependency(benchmark https://github.com/google/benchmark.git v1.9.2)
19+
endif()

worker/include/worker/places.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,46 @@
11
// Copyright 2025 Omkar Prabhu
22
#pragma once
3+
4+
#include <cpr/cpr.h>
5+
6+
#include <memory>
7+
#include <optional>
8+
#include <string>
9+
#include <unordered_map>
10+
#include <utility>
11+
12+
class HttpClientInterface {
13+
public:
14+
virtual ~HttpClientInterface() = default;
15+
virtual cpr::Response Get(const cpr::Url& url,
16+
const cpr::Header& headers) = 0;
17+
};
18+
19+
class Places {
20+
public:
21+
Places() {}
22+
virtual std::unordered_map<std::string, std::string> ReverseGeocode(
23+
const std::string& user_id, const std::string& mediaitem_id,
24+
std::optional<double> latitude, std::optional<double> longitude);
25+
};
26+
27+
class OpenStreetMap : public Places {
28+
public:
29+
OpenStreetMap(std::shared_ptr<HttpClientInterface> http_client,
30+
std::string url =
31+
"https://nominatim.openstreetmap.org/"
32+
"reverse.php?zoom=18&format=jsonv2&lat={lat}&lon={lon}",
33+
int timeout = 60)
34+
: http_client_(http_client), url_(std::move(url)), timeout_(timeout) {}
35+
std::unordered_map<std::string, std::string> ReverseGeocode(
36+
const std::string& user_id, const std::string& mediaitem_id,
37+
std::optional<double> latitude, std::optional<double> longitude) override;
38+
39+
private:
40+
std::shared_ptr<HttpClientInterface> http_client_;
41+
std::string url_;
42+
int timeout_;
43+
std::string Format(
44+
std::string input,
45+
const std::unordered_map<std::string, std::string>& values);
46+
};

worker/src/places.cc

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,118 @@
11
// Copyright 2025 Omkar Prabhu
2+
#include "worker/places.h"
3+
4+
#include <cpr/cpr.h>
5+
#include <simdjson.h>
6+
#include <spdlog/spdlog.h>
7+
8+
#include <memory>
9+
#include <optional>
10+
#include <string>
11+
#include <unordered_map>
12+
#include <utility>
13+
14+
class HttpClient : public HttpClientInterface {
15+
public:
16+
cpr::Response Get(const cpr::Url& url, const cpr::Header& headers) override {
17+
return cpr::Get(url, headers);
18+
}
19+
};
20+
21+
std::unordered_map<std::string, std::string> Places::ReverseGeocode(
22+
const std::string& user_id, const std::string& mediaitem_id,
23+
std::optional<double> latitude, std::optional<double> longitude) {
24+
return {};
25+
}
26+
27+
std::unordered_map<std::string, std::string> OpenStreetMap::ReverseGeocode(
28+
const std::string& user_id, const std::string& mediaitem_id,
29+
std::optional<double> latitude, std::optional<double> longitude) {
30+
if (!latitude.has_value() || !longitude.has_value()) {
31+
return {};
32+
}
33+
34+
cpr::Response r = http_client_->Get(
35+
cpr::Url{
36+
Format(url_, {{"latitude", std::to_string(latitude.value())},
37+
{"longitude", std::to_string(longitude.value())}})},
38+
cpr::Header{{"User-Agent", "smriti-worker"},
39+
{"Accept-Language", "en-GB,en-US"}});
40+
if (r.error.message != "") {
41+
spdlog::error("error in openstreetmap response: {}", r.error.message);
42+
return {};
43+
}
44+
if (r.status_code != 200) {
45+
return {};
46+
}
47+
48+
spdlog::info("success in openstreetmap response: {} {}", r.status_code,
49+
r.text);
50+
51+
simdjson::ondemand::parser parser;
52+
simdjson::padded_string padded_body(r.text);
53+
simdjson::ondemand::document doc = parser.iterate(padded_body);
54+
55+
std::unordered_map<std::string, std::string> result;
56+
57+
if (doc["address"].error() == simdjson::SUCCESS) {
58+
simdjson::ondemand::object address = doc["address"].get_object();
59+
60+
if (address["postcode"].error() == simdjson::SUCCESS) {
61+
result["postcode"] =
62+
std::string(address["postcode"].get_string().value());
63+
}
64+
if (address["country"].error() == simdjson::SUCCESS) {
65+
result["country"] = std::string(address["country"].get_string().value());
66+
}
67+
68+
std::string locality = "";
69+
if (address["city"].error() == simdjson::SUCCESS) {
70+
locality = std::string(address["city"].get_string().value());
71+
}
72+
if (locality == "" && address["town"].error() == simdjson::SUCCESS) {
73+
locality = std::string(address["town"].get_string().value());
74+
}
75+
if (locality == "" && address["village"].error() == simdjson::SUCCESS) {
76+
locality = std::string(address["village"].get_string().value());
77+
}
78+
if (locality == "" &&
79+
address["municipality"].error() == simdjson::SUCCESS) {
80+
locality = std::string(address["municipality"].get_string().value());
81+
}
82+
result["locality"] = locality;
83+
84+
std::string area;
85+
if (address["suburb"].error() == simdjson::SUCCESS) {
86+
area = std::string(address["suburb"].get_string().value());
87+
}
88+
if (area == "" && address["quarter"].error() == simdjson::SUCCESS) {
89+
area = std::string(address["quarter"].get_string().value());
90+
}
91+
if (area == "" && address["neighbourhood"].error() == simdjson::SUCCESS) {
92+
area = std::string(address["neighbourhood"].get_string().value());
93+
}
94+
if (area == "" && address["road"].error() == simdjson::SUCCESS) {
95+
area = std::string(address["road"].get_string().value());
96+
}
97+
result["area"] = area;
98+
} else {
99+
spdlog::error("error in openstreetmap response: {}",
100+
simdjson::error_message(doc["address"].error()));
101+
return {};
102+
}
103+
104+
return result;
105+
}
106+
107+
std::string OpenStreetMap::Format(
108+
std::string input,
109+
const std::unordered_map<std::string, std::string>& values) {
110+
for (const auto& [key, value] : values) {
111+
std::string placeholder = "{" + key + "}";
112+
size_t pos = input.find(placeholder);
113+
if (pos != std::string::npos) {
114+
input.replace(pos, placeholder.length(), value);
115+
}
116+
}
117+
return input;
118+
}

0 commit comments

Comments
 (0)