Compare commits
15 Commits
Author | SHA1 | Date |
---|---|---|
Arthur POULET | 35b69ef33a | |
Arthur POULET | 46d14c2917 | |
Arthur POULET | 3d607ade56 | |
Arthur POULET | 08a2729ce6 | |
Arthur POULET | d4de167df2 | |
Arthur POULET | 29e78b7f52 | |
Arthur POULET | 5f8afb008e | |
Arthur POULET | b9bc73b3b8 | |
Arthur POULET | 8efe5d3ab3 | |
Arthur POULET | db9c5cd7a4 | |
Arthur POULET | 76da1fd1a8 | |
Arthur POULET | 1f44afb893 | |
Arthur POULET | 0485f8131d | |
Arthur POULET | ac29e5bb41 | |
Arthur POULET | 690d41d33f |
|
@ -3,7 +3,7 @@ name: default
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: test
|
- name: test
|
||||||
image: ruby:3.0
|
image: ruby:3.1
|
||||||
environment:
|
environment:
|
||||||
LIFEPEX_DB: "sqlite://test.db"
|
LIFEPEX_DB: "sqlite://test.db"
|
||||||
LIFEPEX_ENV: "test"
|
LIFEPEX_ENV: "test"
|
||||||
|
|
28
Gemfile
28
Gemfile
|
@ -3,34 +3,34 @@
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
|
||||||
# web
|
# web
|
||||||
gem "puma", "~> 5.3"
|
gem "puma", "~> 5"
|
||||||
gem "sinatra", "~> 2.1"
|
gem "sinatra", "~> 2"
|
||||||
gem "sinatra-contrib", "~> 2.1"
|
gem "sinatra-contrib", "~> 2"
|
||||||
gem "slim", "~> 4.1"
|
gem "slim", "~> 4"
|
||||||
|
|
||||||
# database
|
# database
|
||||||
gem "sequel", "~> 5.43"
|
gem "sequel", "~> 5"
|
||||||
# you # comment the drivers you don't want
|
# you # comment the drivers you don't want
|
||||||
gem "sqlite3", "~> 1.4"
|
gem "sqlite3", "~> 1"
|
||||||
# gem "pg", "~> 1.2"
|
# gem "pg", "~> 1.2"
|
||||||
|
|
||||||
# security
|
# security
|
||||||
gem "jwt", "~> 2.2"
|
gem "jwt", "~> 2"
|
||||||
gem "bcrypt", "~> 3.1"
|
gem "bcrypt", "~> 3"
|
||||||
gem "rack_csrf", "~> 2.6"
|
gem "rack_csrf", "~> 2"
|
||||||
|
|
||||||
# api tools
|
# api tools
|
||||||
gem "doc_my_routes"
|
gem "doc_my_routes"
|
||||||
|
|
||||||
# debug and helpers
|
# debug and helpers
|
||||||
gem "colorize", "~> 0.8.1"
|
gem "colorize", "~> 0.8"
|
||||||
gem "activesupport", "= 6.1.3.1"
|
gem "activesupport", "~> 6"
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
group :test do
|
group :test do
|
||||||
gem "pry", "~> 0.14.1"
|
gem "pry"
|
||||||
gem "rack-test", "~> 1.1", require: false
|
gem "rack-test", "~> 1", require: false
|
||||||
# gem "simplecov", "~> 0.21.2", require: false
|
# gem "simplecov", "~> 0.21.2", require: false
|
||||||
end
|
end
|
||||||
|
|
||||||
gem "dotenv", "~> 2.7"
|
gem "dotenv", "~> 2"
|
||||||
|
|
80
Gemfile.lock
80
Gemfile.lock
|
@ -1,83 +1,81 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
activesupport (6.1.3.1)
|
activesupport (6.1.6.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
tzinfo (~> 2.0)
|
tzinfo (~> 2.0)
|
||||||
zeitwerk (~> 2.3)
|
zeitwerk (~> 2.3)
|
||||||
bcrypt (3.1.16)
|
bcrypt (3.1.18)
|
||||||
coderay (1.1.3)
|
coderay (1.1.3)
|
||||||
colorize (0.8.1)
|
colorize (0.8.1)
|
||||||
concurrent-ruby (1.1.9)
|
concurrent-ruby (1.1.10)
|
||||||
doc_my_routes (0.13.0)
|
doc_my_routes (0.13.0)
|
||||||
dotenv (2.7.6)
|
dotenv (2.8.1)
|
||||||
i18n (1.8.10)
|
i18n (1.12.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
jwt (2.2.3)
|
jwt (2.4.1)
|
||||||
method_source (1.0.0)
|
method_source (1.0.0)
|
||||||
minitest (5.14.4)
|
minitest (5.16.2)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
mustermann (1.1.1)
|
mustermann (2.0.2)
|
||||||
ruby2_keywords (~> 0.0.1)
|
ruby2_keywords (~> 0.0.1)
|
||||||
nio4r (2.5.7)
|
nio4r (2.5.8)
|
||||||
pg (1.2.3)
|
|
||||||
pry (0.14.1)
|
pry (0.14.1)
|
||||||
coderay (~> 1.1)
|
coderay (~> 1.1)
|
||||||
method_source (~> 1.0)
|
method_source (~> 1.0)
|
||||||
puma (5.3.2)
|
puma (5.6.4)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
rack (2.2.3)
|
rack (2.2.4)
|
||||||
rack-protection (2.1.0)
|
rack-protection (2.2.2)
|
||||||
rack
|
rack
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rack_csrf (2.6.0)
|
rack_csrf (2.6.0)
|
||||||
rack (>= 1.1.0)
|
rack (>= 1.1.0)
|
||||||
ruby2_keywords (0.0.4)
|
ruby2_keywords (0.0.5)
|
||||||
sequel (5.45.0)
|
sequel (5.58.0)
|
||||||
sinatra (2.1.0)
|
sinatra (2.2.2)
|
||||||
mustermann (~> 1.0)
|
mustermann (~> 2.0)
|
||||||
rack (~> 2.2)
|
rack (~> 2.2)
|
||||||
rack-protection (= 2.1.0)
|
rack-protection (= 2.2.2)
|
||||||
tilt (~> 2.0)
|
tilt (~> 2.0)
|
||||||
sinatra-contrib (2.1.0)
|
sinatra-contrib (2.2.2)
|
||||||
multi_json
|
multi_json
|
||||||
mustermann (~> 1.0)
|
mustermann (~> 2.0)
|
||||||
rack-protection (= 2.1.0)
|
rack-protection (= 2.2.2)
|
||||||
sinatra (= 2.1.0)
|
sinatra (= 2.2.2)
|
||||||
tilt (~> 2.0)
|
tilt (~> 2.0)
|
||||||
slim (4.1.0)
|
slim (4.1.0)
|
||||||
temple (>= 0.7.6, < 0.9)
|
temple (>= 0.7.6, < 0.9)
|
||||||
tilt (>= 2.0.6, < 2.1)
|
tilt (>= 2.0.6, < 2.1)
|
||||||
sqlite3 (1.4.2)
|
sqlite3 (1.4.4)
|
||||||
temple (0.8.2)
|
temple (0.8.2)
|
||||||
tilt (2.0.10)
|
tilt (2.0.11)
|
||||||
tzinfo (2.0.4)
|
tzinfo (2.0.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
zeitwerk (2.4.2)
|
zeitwerk (2.6.0)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
activesupport (= 6.1.3.1)
|
activesupport (~> 6)
|
||||||
bcrypt (~> 3.1)
|
bcrypt (~> 3)
|
||||||
colorize (~> 0.8.1)
|
colorize (~> 0.8)
|
||||||
doc_my_routes
|
doc_my_routes
|
||||||
dotenv (~> 2.7)
|
dotenv (~> 2)
|
||||||
jwt (~> 2.2)
|
jwt (~> 2)
|
||||||
pg (~> 1.2)
|
pry
|
||||||
pry (~> 0.14.1)
|
puma (~> 5)
|
||||||
puma (~> 5.3)
|
rack-test (~> 1)
|
||||||
rack-test (~> 1.1)
|
rack_csrf (~> 2)
|
||||||
rack_csrf (~> 2.6)
|
sequel (~> 5)
|
||||||
sequel (~> 5.43)
|
sinatra (~> 2)
|
||||||
sinatra (~> 2.1)
|
sinatra-contrib (~> 2)
|
||||||
sinatra-contrib (~> 2.1)
|
slim (~> 4)
|
||||||
slim (~> 4.1)
|
sqlite3 (~> 1)
|
||||||
sqlite3 (~> 1.4)
|
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.2.16
|
2.2.16
|
||||||
|
|
|
@ -95,3 +95,9 @@ LIFEPEX_ENV=test rake db:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
Then if you want to run the test, simply type `rake test` (you will need the startup env variable to be set first).
|
Then if you want to run the test, simply type `rake test` (you will need the startup env variable to be set first).
|
||||||
|
|
||||||
|
### Debug
|
||||||
|
|
||||||
|
Some debug options can be enabled with the env variable
|
||||||
|
|
||||||
|
`LIFEPEX_ENV=debug`
|
||||||
|
|
|
@ -70,6 +70,10 @@ h2 {
|
||||||
height: 45px;
|
height: 45px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.smaller {
|
||||||
|
font-size: x-small !important;
|
||||||
|
}
|
||||||
|
|
||||||
.highcharts-figure, .highcharts-data-table table {
|
.highcharts-figure, .highcharts-data-table table {
|
||||||
min-width: 360px;
|
min-width: 360px;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
|
|
|
@ -6,4 +6,18 @@ class LifePex::User < Sequel::Model(:users)
|
||||||
def password=(clear_password)
|
def password=(clear_password)
|
||||||
self.hashed_password = BCrypt::Password.create(clear_password)
|
self.hashed_password = BCrypt::Password.create(clear_password)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def recalls_not_validated(cached: true)
|
||||||
|
@recalls_not_validated = nil if !cached
|
||||||
|
|
||||||
|
pexs = LifePex::Pex.setup_user_pexs(user_id: id, user_pexs: user_pexs)
|
||||||
|
|
||||||
|
@recalls_not_validated ||= recalls.filter do |recall|
|
||||||
|
pex = pexs[recall[:pex_id]]
|
||||||
|
validated = pex[:count_by_date].filter do |date, _|
|
||||||
|
date >= Date.today - recall[:span_duration]
|
||||||
|
end.values.sum
|
||||||
|
validated < recall[:repeated]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,9 +4,9 @@ class LifePex::UserPex < Sequel::Model(:user_pexs)
|
||||||
|
|
||||||
def self.last_inserted_at(user_id)
|
def self.last_inserted_at(user_id)
|
||||||
LifePex::UserPex
|
LifePex::UserPex
|
||||||
.join(:pexs, :id => :pex_id)
|
# .join(:pexs, :id => :pex_id)
|
||||||
.where(Sequel[:user_pexs][:user_id] => user_id)
|
# .where(Sequel[:user_pexs][:user_id] => user_id)
|
||||||
.group(:pex_id)
|
# .group(:pex_id)
|
||||||
.select_append{max(created_at).as(:last_inserted_at)}
|
# .select_append{max(created_at).as(:last_inserted_at)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,33 @@
|
||||||
class LifePex::Systems::AuthSystem < Sinatra::Base
|
class LifePex::Systems::AuthSystem < Sinatra::Base
|
||||||
helpers Sinatra::Cookies
|
helpers Sinatra::Cookies
|
||||||
include JSON::API
|
include JSON::API
|
||||||
|
include LifePex::UsersHelper
|
||||||
|
|
||||||
|
def setup_user_cookie!(user_id)
|
||||||
|
response.set_cookie(
|
||||||
|
"auth",
|
||||||
|
{
|
||||||
|
value: JWT.encode({ "user_id" => user_id }, LifePex::SECRET),
|
||||||
|
expires: Time.now + 2.days,
|
||||||
|
path: "/",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def renew_user_cookie!
|
||||||
|
response.set_cookie(
|
||||||
|
"auth",
|
||||||
|
{
|
||||||
|
value: cookies["auth"],
|
||||||
|
expires: Time.now + 2.days,
|
||||||
|
path: "/",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
def user_id_decoded(cookies = nil)
|
def user_id_decoded(cookies = nil)
|
||||||
cookies = cookies() if cookies.nil?
|
cookies = cookies() if cookies.nil?
|
||||||
begin
|
begin
|
||||||
decoded = JWT.decode(cookies["auth"], LifePex::SECRET)
|
decoded = JWT.decode(cookies["auth"], LifePex::SECRET)
|
||||||
|
renew_user_cookie!
|
||||||
decoded[0]["user_id"]
|
decoded[0]["user_id"]
|
||||||
rescue => err
|
rescue => err
|
||||||
STDERR.puts "user_id_decoded: #{err}"
|
STDERR.puts "user_id_decoded: #{err}"
|
||||||
|
|
|
@ -1,32 +1,14 @@
|
||||||
require "date"
|
require "date"
|
||||||
|
require "active_support/all"
|
||||||
require_relative "./auth.rb"
|
require_relative "./auth.rb"
|
||||||
require_relative "./csrf.rb"
|
require_relative "./csrf.rb"
|
||||||
|
require_relative "../utils/users.rb"
|
||||||
|
|
||||||
class LifePex::Systems::PexSystem < LifePex::Systems::AuthSystem
|
class LifePex::Systems::PexSystem < LifePex::Systems::AuthSystem
|
||||||
include JSON::API
|
|
||||||
include LifePex::Systems::CrlfHelper
|
include LifePex::Systems::CrlfHelper
|
||||||
|
|
||||||
def my_user_pexs(cookies, date = nil)
|
|
||||||
params = {
|
|
||||||
user_id: user_id_decoded(cookies),
|
|
||||||
}
|
|
||||||
params[:created_at] = date unless date.nil?
|
|
||||||
LifePex::UserPex.where(params).all()
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_user_date
|
|
||||||
date = cookies["date"]
|
|
||||||
if date && Date.respond_to?(date)
|
|
||||||
Date.send date
|
|
||||||
elsif date == "yesterday"
|
|
||||||
Date.today - 1
|
|
||||||
else
|
|
||||||
Date.today
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
get "/today", auth: [] do
|
get "/today", auth: [] do
|
||||||
cookies.set "date", { value: "today", httponly: false }
|
cookies.set "date", { value: "now", httponly: false }
|
||||||
redirect "/"
|
redirect "/"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ class LifePex::Systems::Pex2System < LifePex::Systems::PexSystem
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
status_codes [200]
|
status_codes [200]
|
||||||
parameter :pluck, required: false, type: 'string', in: 'query', description: 'improve performance by only fetching one field'
|
parameter :pluck, required: false, type: 'string', in: 'query', description: 'improve performance by only fetching one field'
|
||||||
get '', auth: [], provides: 'json' do
|
get '', auth: [], provides: "json" do
|
||||||
pexs = current_user.pexs
|
pexs = current_user.pexs
|
||||||
pexs = pexs.select(json_params["pluck"]) if json_params["pluck"]
|
pexs = pexs.select(json_params["pluck"]) if json_params["pluck"]
|
||||||
pexs.to_json
|
pexs.to_json
|
||||||
|
|
|
@ -3,7 +3,6 @@ require_relative "./auth.rb"
|
||||||
require_relative "./csrf.rb"
|
require_relative "./csrf.rb"
|
||||||
|
|
||||||
class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem
|
class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem
|
||||||
include JSON::API
|
|
||||||
include LifePex::Systems::CrlfHelper
|
include LifePex::Systems::CrlfHelper
|
||||||
|
|
||||||
DEFAULT_PEXS_FOR_NEW_USERS = YAML.load_file "config/default_pexs_for_new_users.yaml"
|
DEFAULT_PEXS_FOR_NEW_USERS = YAML.load_file "config/default_pexs_for_new_users.yaml"
|
||||||
|
@ -12,14 +11,10 @@ class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem
|
||||||
slim :login
|
slim :login
|
||||||
end
|
end
|
||||||
|
|
||||||
def setup_user_cookie!(user_id)
|
|
||||||
cookies["auth"] = JWT.encode({ "user_id" => user_id }, LifePex::SECRET)
|
|
||||||
end
|
|
||||||
|
|
||||||
def login(params)
|
def login(params)
|
||||||
user = LifePex::User.where(username: params["username"]).first
|
user = LifePex::User.where(username: params["username"]).first
|
||||||
if user && BCrypt::Password.new(user[:hashed_password]) == params["password"]
|
if user && BCrypt::Password.new(user[:hashed_password]) == params["password"]
|
||||||
cookies["auth"] = JWT.encode({ "user_id" => user[:id] }, LifePex::SECRET)
|
setup_user_cookie!(user[:id])
|
||||||
user
|
user
|
||||||
else
|
else
|
||||||
nil
|
nil
|
||||||
|
@ -28,7 +23,6 @@ class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem
|
||||||
|
|
||||||
post "/login", provides: 'html' do
|
post "/login", provides: 'html' do
|
||||||
if user = login(params)
|
if user = login(params)
|
||||||
cookies["auth"] = JWT.encode({ "user_id" => user[:id] }, LifePex::SECRET)
|
|
||||||
redirect "/"
|
redirect "/"
|
||||||
else
|
else
|
||||||
slim :login, locals: { flash: { danger: "Failed to login" } }
|
slim :login, locals: { flash: { danger: "Failed to login" } }
|
||||||
|
@ -89,6 +83,40 @@ class LifePex::Systems::UserSystem < LifePex::Systems::AuthSystem
|
||||||
slim :about
|
slim :about
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get "/preferences" do
|
||||||
|
show_preferences
|
||||||
|
end
|
||||||
|
|
||||||
|
UserPreferenceCookie = Struct.new(:cookie, :allow_blank, :convert, :html, :description)
|
||||||
|
USER_PREFERENCES = {
|
||||||
|
"inputRecapDays" => UserPreferenceCookie.new("recap_days", false, :to_i, { type: "text" }, "Amount of days to show in the recap"),
|
||||||
|
"inputLateDayOffset" => UserPreferenceCookie.new("late_day_offset", false, :to_f, { type: "number", min: "0", max: "24", step: "0.1" }, "Offset for today (so a few hours after midnight is still today)"),
|
||||||
|
"inputShowFullDate" => UserPreferenceCookie.new("show_full_date", true, :to_s, { type: "checkbox" }, "Show or hide the date of the current tab"),
|
||||||
|
}
|
||||||
|
|
||||||
|
post "/preferences" do
|
||||||
|
USER_PREFERENCES.each do |param_name, upc|
|
||||||
|
current_param = params[param_name]
|
||||||
|
next if !upc.allow_blank && current_param.blank?
|
||||||
|
|
||||||
|
cookies[upc.cookie] =
|
||||||
|
case upc.convert
|
||||||
|
when Symbol
|
||||||
|
current_param.send(upc.convert)
|
||||||
|
when Proc
|
||||||
|
upc.convert.call(current_param)
|
||||||
|
else
|
||||||
|
current_param
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
show_preferences
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_preferences
|
||||||
|
slim :preferences
|
||||||
|
end
|
||||||
|
|
||||||
extend DocMyRoutes::Annotatable
|
extend DocMyRoutes::Annotatable
|
||||||
register Sinatra::Namespace
|
register Sinatra::Namespace
|
||||||
namespace '/api/user/v1' do
|
namespace '/api/user/v1' do
|
||||||
|
|
|
@ -3,7 +3,6 @@ require_relative "./api_response"
|
||||||
require "csv"
|
require "csv"
|
||||||
|
|
||||||
class LifePex::Systems::UserPexSystem < LifePex::Systems::AuthSystem
|
class LifePex::Systems::UserPexSystem < LifePex::Systems::AuthSystem
|
||||||
include JSON::API
|
|
||||||
include LifePex::Systems::ApiResponse
|
include LifePex::Systems::ApiResponse
|
||||||
set :protection, :except => [:frame_options, :json_csrf]
|
set :protection, :except => [:frame_options, :json_csrf]
|
||||||
|
|
||||||
|
@ -36,19 +35,21 @@ class LifePex::Systems::UserPexSystem < LifePex::Systems::AuthSystem
|
||||||
|
|
||||||
private def export_csv
|
private def export_csv
|
||||||
pexs = LifePex::Pex.where(user_id: current_user_id).order(:id).all
|
pexs = LifePex::Pex.where(user_id: current_user_id).order(:id).all
|
||||||
pexs_names = pexs.map(&:name)
|
pexs_names = pexs.map { |pex| "[#{pex.category}] #{pex.name}" }
|
||||||
pexs_ids = pexs.map(&:id)
|
pexs_ids = pexs.map(&:id)
|
||||||
user_pexs_by_date = LifePex::UserPex.where(Sequel.qualify(:user_pexs, :user_id) => current_user_id).order_by(:created_at).all.group_by(&:created_at)
|
user_pexs_by_date = LifePex::UserPex
|
||||||
csv = CSV.generate() do |csv|
|
.where(Sequel.qualify(:user_pexs, :user_id) => current_user_id)
|
||||||
|
.order_by(:created_at).all.group_by(&:created_at)
|
||||||
|
csv_output = CSV.generate do |csv|
|
||||||
csv << ["date", *pexs_names]
|
csv << ["date", *pexs_names]
|
||||||
user_pexs_by_date.each do |date, user_pexs|
|
user_pexs_by_date.each do |date, user_pexs|
|
||||||
amount_by_column = user_pexs.group_by(&:pex_id).transform_values{ |v| v.size }
|
amount_by_column = user_pexs.group_by(&:pex_id).transform_values { |v| v.size }
|
||||||
user_pex_amount_by_date_all_column = pexs_ids.map{ |id| amount_by_column[id] || 0}
|
user_pex_amount_by_date_all_column = pexs_ids.map { |id| amount_by_column[id] || 0 }
|
||||||
csv << [date, *user_pex_amount_by_date_all_column]
|
csv << [date, *user_pex_amount_by_date_all_column]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
content_type "application/csv"
|
content_type "application/csv"
|
||||||
csv
|
csv_output
|
||||||
end
|
end
|
||||||
|
|
||||||
namespace "/pexs" do
|
namespace "/pexs" do
|
||||||
|
@ -132,5 +133,6 @@ class LifePex::Systems::UserPexSystem < LifePex::Systems::AuthSystem
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
include LifePex::Systems::ApiList
|
include LifePex::Systems::ApiList
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
require "active_support/all"
|
||||||
|
|
||||||
module JSON::API
|
module JSON::API
|
||||||
def json_params
|
def json_params
|
||||||
begin
|
begin
|
||||||
|
@ -10,18 +12,26 @@ module JSON::API
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def date_input_convertor(date)
|
DATE_GENERATOR = {
|
||||||
if date && Date.respond_to?(date)
|
"yesterday" => ->() { DateTime.now - 24.hours },
|
||||||
Date.send date
|
"now" => ->() { DateTime.now },
|
||||||
elsif date == "yesterday"
|
"today" => ->() { DateTime.now },
|
||||||
Date.today - 1
|
nil => ->() { DateTime.now },
|
||||||
else
|
}
|
||||||
begin
|
# @param [String] date: either a date iso formatted or a word to be sent to DateTime
|
||||||
Date.parse date
|
# @param [Float] offset: an amount of hours to remove from the date, useful for setting the start of the day hours after midnight
|
||||||
rescue => _
|
def date_input_convertor(date = "now", offset = 0.0)
|
||||||
Date.today
|
base_time =
|
||||||
|
if DATE_GENERATOR.key?(date)
|
||||||
|
DATE_GENERATOR[date].call()
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
DateTime.parse date
|
||||||
|
rescue => _
|
||||||
|
DateTime.now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
(base_time - offset.hours).to_date
|
||||||
end
|
end
|
||||||
|
|
||||||
# params:
|
# params:
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
module LifePex::UsersHelper
|
||||||
|
def my_user_pexs(cookies, date = nil)
|
||||||
|
params = {
|
||||||
|
user_id: user_id_decoded(cookies),
|
||||||
|
}
|
||||||
|
params[:created_at] = date unless date.nil?
|
||||||
|
LifePex::UserPex.where(params).all()
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_user_date
|
||||||
|
date = cookies["date"]
|
||||||
|
offset = cookies["late_day_offset"].to_f
|
||||||
|
date_input_convertor(date, offset)
|
||||||
|
end
|
||||||
|
end
|
|
@ -20,9 +20,13 @@ html lang="en"
|
||||||
- if cookies["date"] == "yesterday"
|
- if cookies["date"] == "yesterday"
|
||||||
a.navbar-brand href="/"
|
a.navbar-brand href="/"
|
||||||
| Yesterday
|
| Yesterday
|
||||||
|
- if cookies["show_full_date"] == "on"
|
||||||
|
.smaller= get_user_date
|
||||||
- else
|
- else
|
||||||
a.navbar-brand href="/"
|
a.navbar-brand href="/"
|
||||||
| Today
|
| Today
|
||||||
|
- if cookies["show_full_date"] == "on"
|
||||||
|
.smaller= get_user_date
|
||||||
button.navbar-toggler type="button" data-bs-toggle="collapse" data-bs-target="#navbar-collapser" aria-controls="navbar-collapser" aria-expanded="false" aria-label="Toggle navigation"
|
button.navbar-toggler type="button" data-bs-toggle="collapse" data-bs-target="#navbar-collapser" aria-controls="navbar-collapser" aria-expanded="false" aria-label="Toggle navigation"
|
||||||
span.navbar-toggler-icon/
|
span.navbar-toggler-icon/
|
||||||
.collapse.navbar-collapse#navbar-collapser
|
.collapse.navbar-collapse#navbar-collapser
|
||||||
|
@ -35,7 +39,11 @@ html lang="en"
|
||||||
li.nav-item
|
li.nav-item
|
||||||
a.btn.btn-lg.btn-dark href="/yesterday" Yesterday
|
a.btn.btn-lg.btn-dark href="/yesterday" Yesterday
|
||||||
li.nav-item
|
li.nav-item
|
||||||
a.btn.btn-lg.btn-dark href="/recap" Recap
|
a.btn.btn-lg.btn-dark.position-relative href="/recap"
|
||||||
|
| Recap
|
||||||
|
- if (recalls_count = current_user.recalls_not_validated.count) > 0
|
||||||
|
span.position-absolute.top-0.start-100.translate-middle.badge.rounded-pill.bg-danger
|
||||||
|
= recalls_count
|
||||||
li.nav-item
|
li.nav-item
|
||||||
a.btn.btn-lg.btn-dark href="/achievements" Achievements
|
a.btn.btn-lg.btn-dark href="/achievements" Achievements
|
||||||
li.nav-item
|
li.nav-item
|
||||||
|
@ -44,6 +52,8 @@ html lang="en"
|
||||||
a.btn.btn-lg.btn-dark href="/password" Change password
|
a.btn.btn-lg.btn-dark href="/password" Change password
|
||||||
li.nav-item
|
li.nav-item
|
||||||
a.btn.btn-lg.btn-dark href="/about" About lifepex
|
a.btn.btn-lg.btn-dark href="/about" About lifepex
|
||||||
|
li.nav-item
|
||||||
|
a.btn.btn-lg.btn-dark href="/preferences" Profil preference
|
||||||
li.nav-item
|
li.nav-item
|
||||||
a.btn.btn-lg.btn-dark href="/?filter_hidden=false" Show hidden
|
a.btn.btn-lg.btn-dark href="/?filter_hidden=false" Show hidden
|
||||||
li.nav-item
|
li.nav-item
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
.container
|
||||||
|
h1
|
||||||
|
| Profil preferences
|
||||||
|
|
||||||
|
h2
|
||||||
|
| Recap
|
||||||
|
form(method="POST")
|
||||||
|
== csrf_tag
|
||||||
|
/-
|
||||||
|
- USER_PREFERENCES.each do |param_name, upc|
|
||||||
|
- if upc.html[:type] == "checkbox"
|
||||||
|
.form-group.form-check.form-switch
|
||||||
|
label.col-sm-12.form-check-label for=param_name
|
||||||
|
strong=upc.description
|
||||||
|
- if cookies[upc.cookie] == "on"
|
||||||
|
input.form-check-input *upc.html name=param_name checked="on" /
|
||||||
|
- else
|
||||||
|
input.form-check-input *upc.html name=param_name /
|
||||||
|
- else
|
||||||
|
.form-group.row
|
||||||
|
label.col-sm-12.col-form-label for=param_name
|
||||||
|
strong=upc.description
|
||||||
|
input.form-control.form-control-lg *upc.html name=param_name value=cookies[upc.cookie] /
|
||||||
|
.form-group.row
|
||||||
|
input.btn.btn-lg.btn-block type="submit" value="Update"
|
||||||
|
|
||||||
|
- if LifePex::APP_ENV == "debug"
|
||||||
|
h2
|
||||||
|
| Debug
|
||||||
|
p
|
||||||
|
table
|
||||||
|
- cookies.each do |k, v|
|
||||||
|
tr
|
||||||
|
td= "cookies.#{k}"
|
||||||
|
td= v
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
| get_user_date
|
||||||
|
td= get_user_date
|
||||||
|
|
||||||
|
h2
|
||||||
|
| Private data export
|
||||||
|
a.btn.btn-success(href="/api/user-pex/v1/export.csv")
|
||||||
|
| Export as CSV
|
||||||
|
|
|
@ -26,7 +26,7 @@ h1 Recap
|
||||||
== csrf_tag
|
== csrf_tag
|
||||||
.form-group.row
|
.form-group.row
|
||||||
label.col-form-label for="inputDaysAgo" Load how many days since today ?
|
label.col-form-label for="inputDaysAgo" Load how many days since today ?
|
||||||
input#inputDaysAgo.form-control.form-control-lg name="days_ago" type="number" min="3" max="365" step="1" value=(params["days_ago"] || "60") /
|
input#inputDaysAgo.form-control.form-control-lg name="days_ago" type="number" min="3" max="365" step="1" value=(params["days_ago"] || cookies["recap_days"] || "60") /
|
||||||
.form-group.row
|
.form-group.row
|
||||||
p
|
p
|
||||||
input.btn.btn-lg.btn-block type="submit" value="Reload"
|
input.btn.btn-lg.btn-block type="submit" value="Reload"
|
||||||
|
|
Loading…
Reference in New Issue