mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
Merge pull request #878 from Freika/feature/tiles-usage-stats
Feature/tiles usage stats
This commit is contained in:
commit
2fe142bdf1
12 changed files with 94 additions and 57 deletions
|
|
@ -16,6 +16,7 @@ To set a custom tile URL, go to the user settings and set the `Maps` section to
|
|||
|
||||
- Safe settings for user with default values.
|
||||
- In the user settings, you can now set a custom tile URL for the map. #429 #715
|
||||
- In the user map settings, you can now see a chart of map tiles usage.
|
||||
- If you have Prometheus exporter enabled, you can now see a `ruby_dawarich_map_tiles` metric in Prometheus, which shows the total number of map tiles loaded. Example:
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class Api::V1::Maps::TileUsageController < ApiController
|
||||
def create
|
||||
Maps::TileUsage::Track.new(tile_usage_params[:count].to_i).call
|
||||
Maps::TileUsage::Track.new(current_api_user.id, tile_usage_params[:count].to_i).call
|
||||
|
||||
head :ok
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,13 @@ class Settings::MapsController < ApplicationController
|
|||
|
||||
def index
|
||||
@maps = current_user.safe_settings.maps
|
||||
|
||||
@tile_usage = 7.days.ago.to_date.upto(Time.zone.today).map do |date|
|
||||
[
|
||||
date.to_s,
|
||||
Rails.cache.read("dawarich_map_tiles_usage:#{current_user.id}:#{date}") || 0
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
|
|
|
|||
|
|
@ -34,9 +34,6 @@ import { TileMonitor } from "../maps/tile_monitor";
|
|||
|
||||
export default class extends Controller {
|
||||
static targets = ["container"];
|
||||
static values = {
|
||||
monitoringEnabled: Boolean
|
||||
}
|
||||
|
||||
settingsButtonAdded = false;
|
||||
layerControl = null;
|
||||
|
|
@ -249,7 +246,7 @@ export default class extends Controller {
|
|||
}
|
||||
|
||||
// Initialize tile monitor
|
||||
this.tileMonitor = new TileMonitor(this.monitoringEnabledValue, this.apiKey);
|
||||
this.tileMonitor = new TileMonitor(this.apiKey);
|
||||
|
||||
// Add tile load event handlers to each base layer
|
||||
Object.entries(this.baseMaps()).forEach(([name, layer]) => {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
export class TileMonitor {
|
||||
constructor(monitoringEnabled, apiKey) {
|
||||
this.monitoringEnabled = monitoringEnabled;
|
||||
constructor(apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
this.tileQueue = 0;
|
||||
this.tileUpdateInterval = null;
|
||||
}
|
||||
|
||||
startMonitoring() {
|
||||
// Only start the interval if monitoring is enabled
|
||||
if (!this.monitoringEnabled) return;
|
||||
|
||||
// Clear any existing interval
|
||||
if (this.tileUpdateInterval) {
|
||||
clearInterval(this.tileUpdateInterval);
|
||||
|
|
@ -29,13 +25,11 @@ export class TileMonitor {
|
|||
}
|
||||
|
||||
recordTileLoad() {
|
||||
if (!this.monitoringEnabled) return;
|
||||
this.tileQueue += 1;
|
||||
}
|
||||
|
||||
sendTileUsage() {
|
||||
// Don't send if monitoring is disabled or queue is empty
|
||||
if (!this.monitoringEnabled || this.tileQueue === 0) return;
|
||||
if (this.tileQueue === 0) return;
|
||||
|
||||
const currentCount = this.tileQueue;
|
||||
console.log('Sending tile usage batch:', currentCount);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Maps::TileUsage::Track
|
||||
def initialize(count = 1)
|
||||
def initialize(user_id, count = 1)
|
||||
@user_id = user_id
|
||||
@count = count
|
||||
end
|
||||
|
||||
def call
|
||||
report_to_prometheus
|
||||
report_to_cache
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("Failed to send tile usage metric: #{e.message}")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def report_to_prometheus
|
||||
return unless DawarichSettings.prometheus_exporter_enabled?
|
||||
|
||||
metric_data = {
|
||||
type: 'counter',
|
||||
name: 'dawarich_map_tiles_usage',
|
||||
|
|
@ -13,7 +25,12 @@ class Maps::TileUsage::Track
|
|||
}
|
||||
|
||||
PrometheusExporter::Client.default.send_json(metric_data)
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("Failed to send tile usage metric: #{e.message}")
|
||||
end
|
||||
|
||||
def report_to_cache
|
||||
today_key = "dawarich_map_tiles_usage:#{@user_id}:#{Time.zone.today}"
|
||||
|
||||
current_value = (Rails.cache.read(today_key) || 0).to_i
|
||||
Rails.cache.write(today_key, current_value + @count, expires_in: 7.days)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -96,7 +96,13 @@ class ReverseGeocoding::Places::FetchData
|
|||
end
|
||||
|
||||
def reverse_geocoded_places
|
||||
data = Geocoder.search([place.latitude, place.longitude], limit: 10, distance_sort: true, radius: 10)
|
||||
data = Geocoder.search(
|
||||
[place.latitude, place.longitude],
|
||||
limit: 10,
|
||||
distance_sort: true,
|
||||
radius: 1,
|
||||
units: DISTANCE_UNITS
|
||||
)
|
||||
|
||||
data.reject do |place|
|
||||
place.data['properties']['osm_value'].in?(IGNORED_OSM_VALUES) ||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@
|
|||
data-coordinates="<%= @coordinates %>"
|
||||
data-distance="<%= @distance %>"
|
||||
data-points_number="<%= @points_number %>"
|
||||
data-maps-monitoring-enabled-value="<%= DawarichSettings.prometheus_exporter_enabled? %>"
|
||||
data-timezone="<%= Rails.configuration.time_zone %>">
|
||||
<div data-maps-target="container" class="h-[25rem] rounded-lg w-full min-h-screen">
|
||||
<div id="fog" class="fog"></div>
|
||||
|
|
|
|||
|
|
@ -22,35 +22,46 @@
|
|||
<span>Please remember, that using a custom tile URL may result in extra costs. Check your map tile provider's terms of service for more information.</span>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-5" data-controller="map-preview">
|
||||
<%= form_for :maps,
|
||||
url: settings_maps_path,
|
||||
method: :patch,
|
||||
autocomplete: "off",
|
||||
data: { turbo_method: :patch, turbo: false },
|
||||
class: "lg:col-span-1" do |f| %>
|
||||
<div class="form-control my-2">
|
||||
<%= f.label :name %>
|
||||
<%= f.text_field :name, value: @maps['name'], placeholder: 'Example: OpenStreetMap', class: "input input-bordered" %>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-5" data-controller="map-preview">
|
||||
<div class="flex flex-col gap-4">
|
||||
<%= form_for :maps,
|
||||
url: settings_maps_path,
|
||||
method: :patch,
|
||||
autocomplete: "off",
|
||||
data: { turbo_method: :patch, turbo: false } do |f| %>
|
||||
<div class="form-control my-2">
|
||||
<%= f.label :name %>
|
||||
<%= f.text_field :name, value: @maps['name'], placeholder: 'Example: OpenStreetMap', class: "input input-bordered" %>
|
||||
</div>
|
||||
|
||||
<div class="form-control my-2">
|
||||
<%= f.label :url, 'URL' %>
|
||||
<%= f.text_field :url,
|
||||
value: @maps['url'],
|
||||
autocomplete: "off",
|
||||
placeholder: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
class: "input input-bordered",
|
||||
data: {
|
||||
map_preview_target: "urlInput",
|
||||
action: "input->map-preview#updatePreview"
|
||||
} %>
|
||||
</div>
|
||||
<div class="form-control my-2">
|
||||
<%= f.label :url, 'URL' %>
|
||||
<%= f.text_field :url,
|
||||
value: @maps['url'],
|
||||
autocomplete: "off",
|
||||
placeholder: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
class: "input input-bordered",
|
||||
data: {
|
||||
map_preview_target: "urlInput",
|
||||
action: "input->map-preview#updatePreview"
|
||||
} %>
|
||||
</div>
|
||||
|
||||
<%= f.submit 'Save', class: "btn btn-primary", data: { map_preview_target: "saveButton" } %>
|
||||
<% end %>
|
||||
<%= f.submit 'Save', class: "btn btn-primary", data: { map_preview_target: "saveButton" } %>
|
||||
<% end %>
|
||||
|
||||
<div class="lg:col-span-2" style="height: 500px;">
|
||||
<h2 class="text-lg font-bold">Tile usage</h2>
|
||||
|
||||
<%= line_chart(
|
||||
@tile_usage,
|
||||
height: '200px',
|
||||
xtitle: 'Days',
|
||||
ytitle: 'Tiles',
|
||||
suffix: ' tiles loaded'
|
||||
) %>
|
||||
</div>
|
||||
|
||||
<div style="height: 500px;">
|
||||
<div
|
||||
data-map-preview-target="mapContainer"
|
||||
class="w-full h-full rounded-lg border"
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Reddis
|
||||
def self.client
|
||||
@client ||= Redis.new(url: ENV['REDIS_URL'])
|
||||
end
|
||||
end
|
||||
|
|
@ -6,21 +6,20 @@ RSpec.describe 'Api::V1::Maps::TileUsage', type: :request do
|
|||
describe 'POST /api/v1/maps/tile_usage' do
|
||||
let(:tile_count) { 5 }
|
||||
let(:track_service) { instance_double(Maps::TileUsage::Track) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
allow(Maps::TileUsage::Track).to receive(:new).with(tile_count).and_return(track_service)
|
||||
allow(Maps::TileUsage::Track).to receive(:new).with(user.id, tile_count).and_return(track_service)
|
||||
allow(track_service).to receive(:call)
|
||||
end
|
||||
|
||||
context 'when user is authenticated' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
it 'tracks tile usage' do
|
||||
post '/api/v1/maps/tile_usage',
|
||||
params: { tile_usage: { count: tile_count } },
|
||||
headers: { 'Authorization' => "Bearer #{user.api_key}" }
|
||||
|
||||
expect(Maps::TileUsage::Track).to have_received(:new).with(tile_count)
|
||||
expect(Maps::TileUsage::Track).to have_received(:new).with(user.id, tile_count)
|
||||
expect(track_service).to have_received(:call)
|
||||
expect(response).to have_http_status(:ok)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,16 +5,19 @@ require 'prometheus_exporter/client'
|
|||
|
||||
RSpec.describe Maps::TileUsage::Track do
|
||||
describe '#call' do
|
||||
subject(:track) { described_class.new(tile_count).call }
|
||||
subject(:track) { described_class.new(user_id, tile_count).call }
|
||||
|
||||
let(:user_id) { 1 }
|
||||
let(:tile_count) { 5 }
|
||||
let(:prometheus_client) { instance_double(PrometheusExporter::Client) }
|
||||
|
||||
before do
|
||||
allow(PrometheusExporter::Client).to receive(:default).and_return(prometheus_client)
|
||||
allow(prometheus_client).to receive(:send_json)
|
||||
allow(DawarichSettings).to receive(:prometheus_exporter_enabled?).and_return(true)
|
||||
end
|
||||
|
||||
it 'tracks tile usage' do
|
||||
it 'tracks tile usage in prometheus' do
|
||||
expect(prometheus_client).to receive(:send_json).with(
|
||||
{
|
||||
type: 'counter',
|
||||
|
|
@ -25,5 +28,15 @@ RSpec.describe Maps::TileUsage::Track do
|
|||
|
||||
track
|
||||
end
|
||||
|
||||
it 'tracks tile usage in cache' do
|
||||
expect(Rails.cache).to receive(:write).with(
|
||||
"dawarich_map_tiles_usage:#{user_id}:#{Time.zone.today}",
|
||||
tile_count,
|
||||
expires_in: 7.days
|
||||
)
|
||||
|
||||
track
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue