Add basic stats

This commit is contained in:
Eugene Burmakin 2024-03-23 20:29:55 +01:00
parent 0a156b0966
commit e1193608ef
18 changed files with 175 additions and 2 deletions

View file

@ -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)

View 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

View file

@ -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
View file

@ -0,0 +1,5 @@
class Stat < ApplicationRecord
validates :year, :month, presence: true
belongs_to :user
end

View file

@ -6,4 +6,5 @@ class User < ApplicationRecord
has_many :imports, dependent: :destroy
has_many :points, through: :imports
has_many :stats
end

View 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

View file

@ -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">

View 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>

View 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>

View file

@ -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

View 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

View file

@ -0,0 +1,5 @@
class AddIndexToPointsTimestamp < ActiveRecord::Migration[7.1]
def change
add_index :points, :timestamp
end
end

View 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
View file

@ -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
View 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
View 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

View file

@ -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

View 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