mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
Add basic stats
This commit is contained in:
parent
0a156b0966
commit
e1193608ef
18 changed files with 175 additions and 2 deletions
1
Makefile
1
Makefile
|
|
@ -67,6 +67,7 @@ production_migrate:
|
|||
ssh dokku_frey 'dokku run dawarich bundle exec rails db:migrate'
|
||||
|
||||
build_and_push:
|
||||
git tag -l "$(version)"
|
||||
docker build . -t dawarich:$(version) --platform=linux/amd64
|
||||
docker tag dawarich:$(version) registry.chibi.rodeo/dawarich:$(version)
|
||||
docker push registry.chibi.rodeo/dawarich:$(version)
|
||||
|
|
|
|||
7
app/controllers/stats_controller.rb
Normal file
7
app/controllers/stats_controller.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
class StatsController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
@stats = current_user.stats.group_by(&:year).sort_by { _1 }.reverse
|
||||
end
|
||||
end
|
||||
|
|
@ -7,4 +7,12 @@ module ApplicationHelper
|
|||
'bg-blue-100 text-blue-700 border-blue-300'
|
||||
end
|
||||
end
|
||||
|
||||
def url_time(stat)
|
||||
month = DateTime.new(stat.year, stat.month).in_time_zone(Time.zone)
|
||||
start_at = month.beginning_of_month.to_time.strftime('%Y-%m-%dT%H:%M')
|
||||
end_at = month.end_of_month.to_time.strftime('%Y-%m-%dT%H:%M')
|
||||
|
||||
{ start_at:, end_at: }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
5
app/models/stat.rb
Normal file
5
app/models/stat.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class Stat < ApplicationRecord
|
||||
validates :year, :month, presence: true
|
||||
|
||||
belongs_to :user
|
||||
end
|
||||
|
|
@ -6,4 +6,5 @@ class User < ApplicationRecord
|
|||
|
||||
has_many :imports, dependent: :destroy
|
||||
has_many :points, through: :imports
|
||||
has_many :stats
|
||||
end
|
||||
|
|
|
|||
56
app/services/create_stats.rb
Normal file
56
app/services/create_stats.rb
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
class CreateStats
|
||||
attr_reader :years, :months, :user
|
||||
|
||||
def initialize(user_id)
|
||||
@user = User.find(user_id)
|
||||
@years = (1970..Time.current.year).to_a
|
||||
@months = (1..12).to_a
|
||||
end
|
||||
|
||||
def call
|
||||
years.flat_map do |year|
|
||||
months.map do |month|
|
||||
beginning_of_month_timestamp = DateTime.new(year, month).beginning_of_month.to_i
|
||||
end_of_month_timestamp = DateTime.new(year, month).end_of_month.to_i
|
||||
|
||||
points = points(beginning_of_month_timestamp, end_of_month_timestamp)
|
||||
|
||||
next if points.empty?
|
||||
|
||||
Stat.create(
|
||||
year: year,
|
||||
month: month,
|
||||
distance: distance(points),
|
||||
toponyms: toponyms(points),
|
||||
user: user
|
||||
)
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def points(beginning_of_month_timestamp, end_of_month_timestamp)
|
||||
Point
|
||||
.where(timestamp: beginning_of_month_timestamp..end_of_month_timestamp)
|
||||
.order(:timestamp)
|
||||
.select(:latitude, :longitude, :timestamp, :city, :country)
|
||||
end
|
||||
|
||||
def distance(points)
|
||||
km = 0
|
||||
|
||||
points.each_cons(2) do
|
||||
km += Geocoder::Calculations.distance_between(
|
||||
[_1.latitude, _1.longitude], [_2.latitude, _2.longitude], units: :km
|
||||
)
|
||||
end
|
||||
|
||||
km
|
||||
end
|
||||
|
||||
def toponyms(points)
|
||||
CountriesAndCities.new(points).call
|
||||
end
|
||||
end
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
<ul tabindex="0" class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
|
||||
<li><%= link_to 'Imports', imports_url %></li>
|
||||
<li><%= link_to 'Points', points_url %></li>
|
||||
<li><%= link_to 'Stats', stats_url %></li>
|
||||
</ul>
|
||||
</div>
|
||||
<%= link_to 'Dawarich', root_path, class: 'btn btn-ghost normal-case text-xl'%>
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
<ul class="menu menu-horizontal px-1">
|
||||
<li><%= link_to 'Imports', imports_url %></li>
|
||||
<li><%= link_to 'Points', points_url %></li>
|
||||
<li><%= link_to 'Stats', stats_url %></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
|
|
|
|||
13
app/views/stats/_stat.html.erb
Normal file
13
app/views/stats/_stat.html.erb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<div id="<%= dom_id stat %>" class="card w-full bg-base-200 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">
|
||||
<%= link_to points_url(url_time(stat)), class: 'underline hover:no-underline' do %>
|
||||
<%= "#{Date::MONTHNAMES[stat.month]} of #{stat.year}" %>
|
||||
<% end %>
|
||||
</h2>
|
||||
<p><%= stat.distance %>km</p>
|
||||
<div class="card-actions justify-end">
|
||||
<%= stat.toponyms.count %> countries, <%= stat.toponyms.sum { _1['cities'].count } %> cities
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
10
app/views/stats/index.html.erb
Normal file
10
app/views/stats/index.html.erb
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<div class="w-full">
|
||||
<% @stats.each do |year, stats| %>
|
||||
<h2 class='text-3xl font-bold mt-10'><%= year %></h2>
|
||||
<div class="mt-5 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 gap-6 p-4">
|
||||
<% stats.each do |stat| %>
|
||||
<%= render stat %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
Rails.application.routes.draw do
|
||||
get 'points/index'
|
||||
resources :imports
|
||||
resources :stats, only: :index
|
||||
|
||||
root to: 'home#index'
|
||||
devise_for :users
|
||||
|
||||
|
|
|
|||
15
db/migrate/20240323160300_create_stats.rb
Normal file
15
db/migrate/20240323160300_create_stats.rb
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
class CreateStats < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
create_table :stats do |t|
|
||||
t.integer :year, null: false
|
||||
t.integer :month, null: false
|
||||
t.integer :distance, null: false
|
||||
t.jsonb :toponyms
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
add_index :stats, :year
|
||||
add_index :stats, :month
|
||||
add_index :stats, :distance
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
class AddIndexToPointsTimestamp < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
add_index :points, :timestamp
|
||||
end
|
||||
end
|
||||
5
db/migrate/20240323190039_add_user_id_to_stat.rb
Normal file
5
db/migrate/20240323190039_add_user_id_to_stat.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class AddUserIdToStat < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
add_reference :stats, :user, null: false, foreign_key: true
|
||||
end
|
||||
end
|
||||
18
db/schema.rb
generated
18
db/schema.rb
generated
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.1].define(version: 2024_03_23_125126) do
|
||||
ActiveRecord::Schema[7.1].define(version: 2024_03_23_190039) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
|
|
@ -60,9 +60,24 @@ ActiveRecord::Schema[7.1].define(version: 2024_03_23_125126) do
|
|||
t.index ["country"], name: "index_points_on_country"
|
||||
t.index ["import_id"], name: "index_points_on_import_id"
|
||||
t.index ["latitude", "longitude"], name: "index_points_on_latitude_and_longitude"
|
||||
t.index ["timestamp"], name: "index_points_on_timestamp"
|
||||
t.index ["trigger"], name: "index_points_on_trigger"
|
||||
end
|
||||
|
||||
create_table "stats", force: :cascade do |t|
|
||||
t.integer "year", null: false
|
||||
t.integer "month", null: false
|
||||
t.integer "distance", null: false
|
||||
t.jsonb "toponyms"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.bigint "user_id", null: false
|
||||
t.index ["distance"], name: "index_stats_on_distance"
|
||||
t.index ["month"], name: "index_stats_on_month"
|
||||
t.index ["user_id"], name: "index_stats_on_user_id"
|
||||
t.index ["year"], name: "index_stats_on_year"
|
||||
end
|
||||
|
||||
create_table "users", force: :cascade do |t|
|
||||
t.string "email", default: "", null: false
|
||||
t.string "encrypted_password", default: "", null: false
|
||||
|
|
@ -75,4 +90,5 @@ ActiveRecord::Schema[7.1].define(version: 2024_03_23_125126) do
|
|||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||
end
|
||||
|
||||
add_foreign_key "stats", "users"
|
||||
end
|
||||
|
|
|
|||
8
spec/factories/stats.rb
Normal file
8
spec/factories/stats.rb
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
FactoryBot.define do
|
||||
factory :stat do
|
||||
year { 1 }
|
||||
month { 1 }
|
||||
distance { 1 }
|
||||
toponyms { "" }
|
||||
end
|
||||
end
|
||||
6
spec/models/stat_spec.rb
Normal file
6
spec/models/stat_spec.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Stat, type: :model do
|
||||
it { is_expected.to validate_presence_of(:year) }
|
||||
it { is_expected.to validate_presence_of(:month) }
|
||||
end
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe User, type: :model do
|
||||
it { is_expected.to have_many(:imports).dependent(:destroy) }
|
||||
it { is_expected.to have_many(:points).through(:imports) }
|
||||
it { is_expected.to have_many(:stats) }
|
||||
end
|
||||
|
|
|
|||
11
spec/requests/stats_spec.rb
Normal file
11
spec/requests/stats_spec.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe "/stats", type: :request do
|
||||
|
||||
describe "GET /index" do
|
||||
it "renders a successful response" do
|
||||
get stats_url
|
||||
expect(response.status).to eq(302)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue