Compare commits

...

16 Commits

Author SHA1 Message Date
Arthur POULET 35b69ef33a
Loosen on dependencies version
continuous-integration/drone/push Build is passing Details
2022-07-28 00:47:13 +02:00
Arthur POULET 46d14c2917
Update dependencies (security)
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build is failing Details
2022-07-28 00:14:35 +02:00
Arthur POULET 3d607ade56 deps: execute bundle update 2022-06-20 23:48:15 +02:00
Arthur POULET 08a2729ce6
Merge remote-tracking branch 'origin/develop' into develop
continuous-integration/drone/push Build is passing Details
2022-04-23 11:30:35 +02:00
Arthur POULET d4de167df2
Merge remote-tracking branch 'origin/master'
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2022-04-23 11:28:44 +02:00
Arthur POULET 29e78b7f52
cookies: add a duration for cookies 2022-04-23 11:27:55 +02:00
Arthur POULET 5f8afb008e drone: update ruby image
continuous-integration/drone/push Build is passing Details
2022-03-27 17:27:39 +02:00
Arthur POULET b9bc73b3b8 recalls: add a red pill to remind recalls to do
closes #61
2022-03-27 17:23:31 +02:00
Arthur POULET 8efe5d3ab3
offset: fix offset generator for yesterday 2022-03-19 11:13:47 +01:00
Arthur POULET db9c5cd7a4 prefs: add show full date checkbox
* factorize user prefs cookies
* add checkboxe
2022-02-28 23:47:14 +01:00
Arthur POULET 76da1fd1a8 systems: fix UserHelper inclusion 2022-02-28 22:56:26 +01:00
Arthur POULET 1f44afb893 layout: add the full date (today/yesterday) 2022-02-28 22:52:57 +01:00
Arthur POULET 0485f8131d
prefs: add offset as a user pref 2022-02-26 16:26:46 +01:00
Arthur POULET ac29e5bb41 preferences: add a page to edit user prefs
closes #63
2022-01-11 05:25:39 +01:00
Arthur POULET 690d41d33f csv: improve csv headers with category 2022-01-11 05:06:47 +01:00
Arthur POULET fef976ed5c csv: add csv export in /about 2022-01-05 23:02:25 +01:00
18 changed files with 287 additions and 104 deletions

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,57 @@
require_relative "./auth.rb" require_relative "./auth.rb"
require_relative "./api_response" require_relative "./api_response"
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]
extend DocMyRoutes::Annotatable # included by PexSystem extend DocMyRoutes::Annotatable # included by PexSystem
register Sinatra::Namespace # included by PexSystem register Sinatra::Namespace # included by PexSystem
namespace "/api/user-pex/v1" do namespace "/api/user-pex/v1" do
summary "Export every single user pex and pex"
produces "application/json,application/csv"
status_codes [200]
# parameter :id, required: true, type: "integer", in: "path"
# parameter :date, required: true, type: "string", in: "path"
get "/export", auth: [], provides: %w(json application/csv) do
if accept? "application/csv"
export_csv
else
pexs = LifePex::Pex.where(user_id: current_user_id).map{ |pex| [pex, pex.user_pexs] }.to_h
api_response({ pexs: pexs, entity_type: "pexs" })
end
end
summary "Export every single user pex and pex as CSV"
produces "application/csv"
status_codes [200]
# parameter :id, required: true, type: "integer", in: "path"
# parameter :date, required: true, type: "string", in: "path"
get "/export.csv", auth: [], provides: %w(application/csv) do
export_csv
end
private def export_csv
pexs = LifePex::Pex.where(user_id: current_user_id).order(:id).all
pexs_names = pexs.map { |pex| "[#{pex.category}] #{pex.name}" }
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)
csv_output = CSV.generate do |csv|
csv << ["date", *pexs_names]
user_pexs_by_date.each do |date, user_pexs|
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 }
csv << [date, *user_pex_amount_by_date_all_column]
end
end
content_type "application/csv"
csv_output
end
namespace "/pexs" do namespace "/pexs" do
summary "Get the amount of user_pex for a given day" summary "Get the amount of user_pex for a given day"
@ -90,5 +133,6 @@ class LifePex::Systems::UserPexSystem < LifePex::Systems::AuthSystem
end end
end end
include LifePex::Systems::ApiList include LifePex::Systems::ApiList
end end

View File

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

15
src/utils/users.rb Normal file
View File

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

View File

@ -17,7 +17,10 @@
I engage my honor to do never read or modify personnal data you may have put on I engage my honor to do never read or modify personnal data you may have put on
the server, and do my best to ensure its security. the server, and do my best to ensure its security.
You should look at the code source if you want to audit it. You should look at the code source if you want to audit it.
br
h2
| GDPR
p
| In case you want to take a look at your data or want to delete it, I may in the futur | In case you want to take a look at your data or want to delete it, I may in the futur
provide a feature for do it yourself. provide a feature for do it yourself.
Meanwhile you can still drop me an issue or a message on &nbsp; Meanwhile you can still drop me an issue or a message on &nbsp;
@ -26,6 +29,8 @@
| &nbsp; | &nbsp;
i i
| #lifepex. | #lifepex.
a.btn.btn-success(href="/api/user-pex/v1/export.csv")
| Export as CSV
h2 h2
| Service version | Service version

View File

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

View File

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

View File

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