Added test_defaults_seed_and_load_into_empty_project
Added :defaults scopes to models Added :sources and :formulas fixtures Loading defaults from seeds.rb using rake task instead of migration
This commit is contained in:
parent
8f2a455561
commit
1b7f2f0abd
@ -13,7 +13,7 @@ class BodyTrackersController < ApplicationController
|
|||||||
|
|
||||||
# Units
|
# Units
|
||||||
available_units = @project.units.pluck(:shortname, :id).to_h
|
available_units = @project.units.pluck(:shortname, :id).to_h
|
||||||
defaults = Unit.where(project: nil).map do |u|
|
defaults = Unit.defaults.map do |u|
|
||||||
u.attributes.except('id', 'project_id', 'created_at', 'updated_at')
|
u.attributes.except('id', 'project_id', 'created_at', 'updated_at')
|
||||||
end
|
end
|
||||||
defaults.delete_if { |u| available_units.has_key?(u['shortname']) }
|
defaults.delete_if { |u| available_units.has_key?(u['shortname']) }
|
||||||
@ -26,7 +26,7 @@ class BodyTrackersController < ApplicationController
|
|||||||
# Quantities
|
# Quantities
|
||||||
available_quantities = Quantity.each_with_path(@project.quantities).map(&:rotate).to_h
|
available_quantities = Quantity.each_with_path(@project.quantities).map(&:rotate).to_h
|
||||||
quantities_count = available_quantities.length
|
quantities_count = available_quantities.length
|
||||||
defaults = Quantity.where(project: nil)
|
defaults = Quantity.defaults
|
||||||
Quantity.each_with_path(defaults) do |q, path|
|
Quantity.each_with_path(defaults) do |q, path|
|
||||||
unless available_quantities.has_key?(path)
|
unless available_quantities.has_key?(path)
|
||||||
attrs = q.attributes.except('id', 'project_id', 'parent_id', 'lft', 'rgt',
|
attrs = q.attributes.except('id', 'project_id', 'parent_id', 'lft', 'rgt',
|
||||||
@ -56,7 +56,7 @@ class BodyTrackersController < ApplicationController
|
|||||||
|
|
||||||
# Sources
|
# Sources
|
||||||
available_sources = @project.sources.pluck(:name, :id).to_h
|
available_sources = @project.sources.pluck(:name, :id).to_h
|
||||||
defaults = Source.where(project: nil).map do |s|
|
defaults = Source.defaults.map do |s|
|
||||||
s.attributes.except('id', 'project_id', 'created_at', 'updated_at')
|
s.attributes.except('id', 'project_id', 'created_at', 'updated_at')
|
||||||
end
|
end
|
||||||
defaults.delete_if { |s| available_sources.has_key?(s['name']) }
|
defaults.delete_if { |s| available_sources.has_key?(s['name']) }
|
||||||
|
@ -6,6 +6,8 @@ class Formula < ActiveRecord::Base
|
|||||||
belongs_to :quantity, inverse_of: :formula, required: true
|
belongs_to :quantity, inverse_of: :formula, required: true
|
||||||
belongs_to :unit
|
belongs_to :unit
|
||||||
|
|
||||||
|
scope :defaults, -> { includes(:quantity).where(quantities: {project: nil}) }
|
||||||
|
|
||||||
validates :code, presence: true
|
validates :code, presence: true
|
||||||
validate do
|
validate do
|
||||||
messages = parse.each { |message, params| errors.add(:code, message, params) }
|
messages = parse.each { |message, params| errors.add(:code, message, params) }
|
||||||
@ -92,7 +94,7 @@ class Formula < ActiveRecord::Base
|
|||||||
# e.g. during import of defaults (so impossible to use recursive sql instead)
|
# e.g. during import of defaults (so impossible to use recursive sql instead)
|
||||||
q_names = identifiers.map { |i| i.split('::').last }
|
q_names = identifiers.map { |i| i.split('::').last }
|
||||||
q_paths = {}
|
q_paths = {}
|
||||||
(quantity.project.try(&:quantities) || Quantity.where(project: nil))
|
(quantity.project.try(&:quantities) || Quantity.defaults)
|
||||||
.select { |q| q_names.include?(q.name) }.each do |q|
|
.select { |q| q_names.include?(q.name) }.each do |q|
|
||||||
|
|
||||||
# NOTE: after upgrade to Ruby 2.7 replace with Enumerator#produce
|
# NOTE: after upgrade to Ruby 2.7 replace with Enumerator#produce
|
||||||
|
@ -13,6 +13,8 @@ class Quantity < ActiveRecord::Base
|
|||||||
has_many :values, class_name: 'QuantityValue', dependent: :restrict_with_error
|
has_many :values, class_name: 'QuantityValue', dependent: :restrict_with_error
|
||||||
has_many :exposures, dependent: :destroy
|
has_many :exposures, dependent: :destroy
|
||||||
|
|
||||||
|
scope :defaults, -> { where(project: nil) }
|
||||||
|
|
||||||
has_one :formula, inverse_of: :quantity, dependent: :destroy, validate: true
|
has_one :formula, inverse_of: :quantity, dependent: :destroy, validate: true
|
||||||
accepts_nested_attributes_for :formula, allow_destroy: true,
|
accepts_nested_attributes_for :formula, allow_destroy: true,
|
||||||
reject_if: proc { |attrs| attrs['id'].blank? && attrs['code'].blank? }
|
reject_if: proc { |attrs| attrs['id'].blank? && attrs['code'].blank? }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
class Source < ActiveRecord::Base
|
class Source < ActiveRecord::Base
|
||||||
belongs_to :project, required: false
|
belongs_to :project, required: false
|
||||||
|
|
||||||
|
scope :defaults, -> { where(project: nil) }
|
||||||
|
|
||||||
validates :name, presence: true, uniqueness: {scope: :project_id}
|
validates :name, presence: true, uniqueness: {scope: :project_id}
|
||||||
|
|
||||||
# Has to go before any 'dependent:' association
|
# Has to go before any 'dependent:' association
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
class Unit < ActiveRecord::Base
|
class Unit < ActiveRecord::Base
|
||||||
belongs_to :project, required: false
|
belongs_to :project, required: false
|
||||||
|
|
||||||
|
scope :defaults, -> { where(project: nil) }
|
||||||
|
|
||||||
validates :shortname, presence: true, uniqueness: {scope: :project_id}
|
validates :shortname, presence: true, uniqueness: {scope: :project_id}
|
||||||
|
|
||||||
# Has to go before any 'dependent:' association
|
# Has to go before any 'dependent:' association
|
||||||
|
194
db/seeds.rb
Normal file
194
db/seeds.rb
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
# Formulas will be deleted as dependent on Quantities
|
||||||
|
[Source, Quantity, Unit].each { |model| model.defaults.delete_all }
|
||||||
|
|
||||||
|
# Units
|
||||||
|
u_a = Unit.create shortname: "g", name: "gram"
|
||||||
|
u_aa = Unit.create shortname: "ug", name: "microgram"
|
||||||
|
u_ab = Unit.create shortname: "mg", name: "milligram"
|
||||||
|
u_ac = Unit.create shortname: "kg", name: "kilogram"
|
||||||
|
u_b = Unit.create shortname: "kcal", name: "kilocalorie"
|
||||||
|
u_c = Unit.create shortname: "%", name: "percent"
|
||||||
|
|
||||||
|
# Quantities
|
||||||
|
# https://www.fsai.ie/uploadedFiles/Consol_Reg1169_2011.pdf
|
||||||
|
# https://www.fsai.ie/legislation/food_legislation/food_information_fic/nutrition_labelling.html
|
||||||
|
# -> Energy
|
||||||
|
e_a = Quantity.create name: "Energy", domain: :diet, parent: nil,
|
||||||
|
description: "Total energy"
|
||||||
|
e_aa = Quantity.create name: "calculated", domain: :diet, parent: e_a,
|
||||||
|
description: "Total energy calculated from macronutrients"
|
||||||
|
e_ab = Quantity.create name: "as %RM", domain: :diet, parent: e_a,
|
||||||
|
description: "Total energy percent value relative to current" \
|
||||||
|
" resting metabolism"
|
||||||
|
e_ac = Quantity.create name: "proteins", domain: :diet, parent: e_a,
|
||||||
|
description: "Calculated proteins energy"
|
||||||
|
e_aca = Quantity.create name: "as %RM", domain: :diet, parent: e_ac,
|
||||||
|
description: ""
|
||||||
|
e_ad = Quantity.create name: "fats", domain: :diet, parent: e_a,
|
||||||
|
description: "Calculated fats energy"
|
||||||
|
e_ada = Quantity.create name: "as %RM", domain: :diet, parent: e_ad,
|
||||||
|
description: ""
|
||||||
|
e_ae = Quantity.create name: "carbs", domain: :diet, parent: e_a,
|
||||||
|
description: "Calculated carbs energy"
|
||||||
|
e_aea = Quantity.create name: "as %RM", domain: :diet, parent: e_ae,
|
||||||
|
description: ""
|
||||||
|
|
||||||
|
# -> Proteins
|
||||||
|
p_a = Quantity.create name: "Proteins", domain: :diet, parent: nil,
|
||||||
|
description: "Total amount of proteins"
|
||||||
|
|
||||||
|
# -> Fats
|
||||||
|
f_a = Quantity.create name: "Fats", domain: :diet, parent: nil,
|
||||||
|
description: "Total lipids, including phospholipids"
|
||||||
|
f_aa = Quantity.create name: "Fatty acids", domain: :diet, parent: f_a,
|
||||||
|
description: ""
|
||||||
|
f_aaa = Quantity.create name: "Saturated", domain: :diet, parent: f_aa,
|
||||||
|
description: "Fatty acids without double bond"
|
||||||
|
f_aab = Quantity.create name: "Unsaturated", domain: :diet, parent: f_aa,
|
||||||
|
description: ""
|
||||||
|
f_aaba = Quantity.create name: "Monounsaturated", domain: :diet, parent: f_aab,
|
||||||
|
description: "Fatty acids with one cis double bond"
|
||||||
|
f_aabb = Quantity.create name: "Polyunsaturated", domain: :diet, parent: f_aab,
|
||||||
|
description: "Fatty acids with two or more cis, cis-methylene" \
|
||||||
|
" interrupted double bonds; PUFA"
|
||||||
|
f_aabba = Quantity.create name: "Omega-3 (n-3)", domain: :diet, parent: f_aabb,
|
||||||
|
description: ""
|
||||||
|
f_aabbaa = Quantity.create name: "ALA 18:3(n-3)", domain: :diet, parent: f_aabba,
|
||||||
|
description: "alpha-Linolenic acid"
|
||||||
|
f_aabbab = Quantity.create name: "EPA 20:5(n-3)", domain: :diet, parent: f_aabba,
|
||||||
|
description: "Eicosapentaenoic acid; also icosapentaenoic acid"
|
||||||
|
f_aabbac = Quantity.create name: "DHA 22:6(n-3)", domain: :diet, parent: f_aabba,
|
||||||
|
description: "Docosahexaenoic acid"
|
||||||
|
f_aabbb = Quantity.create name: "Omega-6 (n-6)", domain: :diet, parent: f_aabb,
|
||||||
|
description: ""
|
||||||
|
f_aabc = Quantity.create name: "Trans", domain: :diet, parent: f_aab,
|
||||||
|
description: "Fatty acids with at least one non-conjugated C-C" \
|
||||||
|
" double bond in the trans configuration"
|
||||||
|
|
||||||
|
# -> Carbs
|
||||||
|
c_a = Quantity.create name: "Carbs", domain: :diet, parent: nil,
|
||||||
|
description: "Total amount of carbohydrates"
|
||||||
|
c_aa = Quantity.create name: "Digestible", domain: :diet, parent: c_a,
|
||||||
|
description: ""
|
||||||
|
c_aaa = Quantity.create name: "Sugars", domain: :diet, parent: c_aa,
|
||||||
|
description: "Monosaccharides and disaccharides, excluding" \
|
||||||
|
" polyols"
|
||||||
|
c_aaaa = Quantity.create name: "Monosaccharides", domain: :diet, parent: c_aaa,
|
||||||
|
description: ""
|
||||||
|
c_aaaaa = Quantity.create name: "Glucose", domain: :diet, parent: c_aaaa,
|
||||||
|
description: ""
|
||||||
|
c_aaaab = Quantity.create name: "Fructose", domain: :diet, parent: c_aaaa,
|
||||||
|
description: ""
|
||||||
|
c_aaab = Quantity.create name: "Disaccharides", domain: :diet, parent: c_aaa,
|
||||||
|
description: ""
|
||||||
|
c_aaaba = Quantity.create name: "Sucrose", domain: :diet, parent: c_aaab,
|
||||||
|
description: ""
|
||||||
|
c_aaabb = Quantity.create name: "Lactose", domain: :diet, parent: c_aaab,
|
||||||
|
description: ""
|
||||||
|
c_aab = Quantity.create name: "Polyols", domain: :diet, parent: c_aa,
|
||||||
|
description: "Alcohols containing more than 2 hydroxyl groups"
|
||||||
|
c_aac = Quantity.create name: "Polysaccharides", domain: :diet, parent: c_aa,
|
||||||
|
description: ""
|
||||||
|
c_aaca = Quantity.create name: "Starch", domain: :diet, parent: c_aac,
|
||||||
|
description: ""
|
||||||
|
c_ab = Quantity.create name: "Indigestible", domain: :diet, parent: c_a,
|
||||||
|
description: ""
|
||||||
|
c_aba = Quantity.create name: "Fibre", domain: :diet, parent: c_ab,
|
||||||
|
description: "Carbohydrate polymers with 3 or more monomeric" \
|
||||||
|
" units, which are neither digested nor absorbed" \
|
||||||
|
" in the human small intestine"
|
||||||
|
|
||||||
|
# -> Minerals
|
||||||
|
m_a = Quantity.create name: "Minerals", domain: :diet, parent: nil,
|
||||||
|
description: ""
|
||||||
|
m_aa = Quantity.create name: "Salt", domain: :diet, parent: m_a,
|
||||||
|
description: "Sodium chloride"
|
||||||
|
|
||||||
|
# -> Vitamins
|
||||||
|
v_a = Quantity.create name: "Vitamins", domain: :diet, parent: nil,
|
||||||
|
description: ""
|
||||||
|
v_aa = Quantity.create name: "Vitamin A", domain: :diet, parent: v_a,
|
||||||
|
description: ""
|
||||||
|
v_aaa = Quantity.create name: "Retinol (A1)", domain: :diet, parent: v_aa,
|
||||||
|
description: ""
|
||||||
|
v_ab = Quantity.create name: "Provitamin A", domain: :diet, parent: v_a,
|
||||||
|
description: ""
|
||||||
|
v_aba = Quantity.create name: "beta-Carotene", domain: :diet, parent: v_ab,
|
||||||
|
description: ""
|
||||||
|
v_ac = Quantity.create name: "Vitamin B", domain: :diet, parent: v_a,
|
||||||
|
description: ""
|
||||||
|
v_aca = Quantity.create name: "Thiamine (B1)", domain: :diet, parent: v_ac,
|
||||||
|
description: ""
|
||||||
|
v_acb = Quantity.create name: "Riboflavin (B2)", domain: :diet, parent: v_ac,
|
||||||
|
description: "Vitamin G"
|
||||||
|
v_acc = Quantity.create name: "Vitamin B3", domain: :diet, parent: v_ac,
|
||||||
|
description: "Vitamin PP"
|
||||||
|
v_acca = Quantity.create name: "Niacin", domain: :diet, parent: v_acc,
|
||||||
|
description: "Nicotinic acid"
|
||||||
|
v_acd = Quantity.create name: "Vitamin B5", domain: :diet, parent: v_ac,
|
||||||
|
description: "Pantothenic acid"
|
||||||
|
v_ace = Quantity.create name: "Vitamin B6", domain: :diet, parent: v_ac,
|
||||||
|
description: ""
|
||||||
|
v_acf = Quantity.create name: "Biotin (B7)", domain: :diet, parent: v_ac,
|
||||||
|
description: "Vitamin H, also coenzyme R"
|
||||||
|
v_acg = Quantity.create name: "Folate", domain: :diet, parent: v_ac,
|
||||||
|
description: "Includes: folic acid, folacin and vitamin B9"
|
||||||
|
v_acga = Quantity.create name: "Vitamin B9", domain: :diet, parent: v_acg,
|
||||||
|
description: ""
|
||||||
|
v_ach = Quantity.create name: "Cobalamin (B12)", domain: :diet, parent: v_ac,
|
||||||
|
description: ""
|
||||||
|
v_ad = Quantity.create name: "Vitamin C", domain: :diet, parent: v_a,
|
||||||
|
description: ""
|
||||||
|
v_ae = Quantity.create name: "Vitamin D", domain: :diet, parent: v_a,
|
||||||
|
description: "Calciferol"
|
||||||
|
v_aea = Quantity.create name: "Cholecalciferol (D3)", domain: :diet, parent: v_ae,
|
||||||
|
description: ""
|
||||||
|
v_af = Quantity.create name: "Vitamin E", domain: :diet, parent: v_a,
|
||||||
|
description: ""
|
||||||
|
v_ag = Quantity.create name: "Vitamin K", domain: :diet, parent: v_a,
|
||||||
|
description: ""
|
||||||
|
|
||||||
|
# -> Body composition
|
||||||
|
b_a = Quantity.create name: "Body composition", domain: :measurement, parent: nil,
|
||||||
|
description: ""
|
||||||
|
b_aa = Quantity.create name: "Weight", domain: :measurement, parent: b_a,
|
||||||
|
description: "Total weight"
|
||||||
|
b_aaa = Quantity.create name: "Fat", domain: :measurement, parent: b_aa,
|
||||||
|
description: "Fat weight"
|
||||||
|
b_aab = Quantity.create name: "Muscle", domain: :measurement, parent: b_aa,
|
||||||
|
description: "Muscle weight"
|
||||||
|
b_ab = Quantity.create name: "Composition", domain: :measurement, parent: b_a,
|
||||||
|
description: ""
|
||||||
|
b_aba = Quantity.create name: "% fat", domain: :measurement, parent: b_ab,
|
||||||
|
description: "Fat as a % of total body weight"
|
||||||
|
b_abb = Quantity.create name: "% muscle", domain: :measurement, parent: b_ab,
|
||||||
|
description: "Muscle as a % of total body weight"
|
||||||
|
b_ac = Quantity.create name: "RM", domain: :measurement, parent: b_a,
|
||||||
|
description: "Resting metabolism"
|
||||||
|
b_ad = Quantity.create name: "VF", domain: :measurement, parent: b_a,
|
||||||
|
description: "Visceral fat"
|
||||||
|
|
||||||
|
# Formulas go at the and to make sure dependencies exist
|
||||||
|
e_aa.create_formula zero_nil: true, unit: u_b,
|
||||||
|
code: "4*Proteins + 9*Fats + 4*Carbs + 2*Fibre"
|
||||||
|
e_ab.create_formula zero_nil: true, unit: u_c,
|
||||||
|
code: "100*Energy/RM.lastBefore(Meal.eaten_at||Meal.created_at)"
|
||||||
|
e_ac.create_formula zero_nil: true, unit: u_b,
|
||||||
|
code: "4*Proteins"
|
||||||
|
e_aca.create_formula zero_nil: true, unit: u_c,
|
||||||
|
code: "100*proteins/RM.lastBefore(Meal.eaten_at||Meal.created_at)"
|
||||||
|
e_ad.create_formula zero_nil: true, unit: u_b,
|
||||||
|
code: "4*Fats"
|
||||||
|
e_ada.create_formula zero_nil: true, unit: u_c,
|
||||||
|
code: "100*fats/RM.lastBefore(Meal.eaten_at||Meal.created_at)"
|
||||||
|
e_ae.create_formula zero_nil: true, unit: u_b,
|
||||||
|
code: "4*Carbs"
|
||||||
|
e_aea.create_formula zero_nil: true, unit: u_c,
|
||||||
|
code: "100*carbs/RM.lastBefore(Meal.eaten_at||Meal.created_at)"
|
||||||
|
|
||||||
|
b_aaa.create_formula zero_nil: true, unit: u_ac,
|
||||||
|
code: "'% fat' * Weight"
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
s_a = Source.create name: "nutrition label",
|
||||||
|
description: "nutrition facts taken from package nutrition label"
|
20
lib/tasks/body_tracking.rake
Normal file
20
lib/tasks/body_tracking.rake
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
require Rails.root.join('config', 'environment')
|
||||||
|
|
||||||
|
namespace :redmine do
|
||||||
|
namespace :body_tracking do
|
||||||
|
desc "Loads body_tracking plugin seed data from db/seeds.rb. Requires pending" \
|
||||||
|
" migrations to be applied before running. Purges and reloads all seed data."
|
||||||
|
task seed: [:environment, 'db:abort_if_pending_migrations'] do
|
||||||
|
seed_fn = Rails.root.join('plugins', 'body_tracking', 'db', 'seeds.rb')
|
||||||
|
if seed_fn.exist?
|
||||||
|
print "Loading seed data from #{seed_fn}..."
|
||||||
|
load(seed_fn)
|
||||||
|
puts "done"
|
||||||
|
else
|
||||||
|
puts "Seed data file #{seed_fn} is missing :/"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -25,7 +25,7 @@ class BodyTrackingSystemTestCase < ApplicationSystemTestCase
|
|||||||
# Redmine fixtures use explicit IDs, so it's impossible to access them by name.
|
# Redmine fixtures use explicit IDs, so it's impossible to access them by name.
|
||||||
# Use: 'project_id: 1' and NOT 'project: projects_001'
|
# Use: 'project_id: 1' and NOT 'project: projects_001'
|
||||||
plugin_fixtures :enabled_modules, :roles, :member_roles,
|
plugin_fixtures :enabled_modules, :roles, :member_roles,
|
||||||
:quantities, :units, :goals, :exposures, :targets, :quantity_values
|
:sources, :quantities, :units, :formulas, :goals, :exposures, :targets, :quantity_values
|
||||||
|
|
||||||
include AbstractController::Translation
|
include AbstractController::Translation
|
||||||
|
|
||||||
|
0
test/fixtures/formulas.yml
vendored
Normal file
0
test/fixtures/formulas.yml
vendored
Normal file
0
test/fixtures/sources.yml
vendored
Normal file
0
test/fixtures/sources.yml
vendored
Normal file
@ -1,4 +1,5 @@
|
|||||||
require File.expand_path('../../application_system_test_case', __FILE__)
|
require File.expand_path('../../application_system_test_case', __FILE__)
|
||||||
|
require 'rake'
|
||||||
|
|
||||||
class BodyTrackersTest < BodyTrackingSystemTestCase
|
class BodyTrackersTest < BodyTrackingSystemTestCase
|
||||||
def setup
|
def setup
|
||||||
@ -7,12 +8,24 @@ class BodyTrackersTest < BodyTrackingSystemTestCase
|
|||||||
log_user 'jsmith', 'jsmith'
|
log_user 'jsmith', 'jsmith'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_defaults_load
|
def test_defaults_seed_and_load_into_empty_project
|
||||||
|
Rails.application.load_tasks
|
||||||
|
Rake::Task['redmine:body_tracking:seed'].invoke
|
||||||
|
counts = [Source, Quantity, Formula, Unit].map do |model|
|
||||||
|
assoc = model.to_s.downcase.pluralize
|
||||||
|
@project1.send(assoc).delete_all unless assoc == 'formulas'
|
||||||
|
["@project1.#{assoc}.reload.count", model.defaults.count]
|
||||||
|
end.to_h
|
||||||
|
|
||||||
visit project_body_trackers_path(@project1)
|
visit project_body_trackers_path(@project1)
|
||||||
|
assert_difference counts do
|
||||||
accept_alert t('layouts.sidebar.confirm_defaults') do
|
accept_alert t('layouts.sidebar.confirm_defaults') do
|
||||||
click_link t('layouts.sidebar.link_defaults')
|
click_link t('layouts.sidebar.link_defaults')
|
||||||
end
|
end
|
||||||
|
# click_link is asynchronuous, need to wait for page reload before
|
||||||
|
# checking differences
|
||||||
assert_selector 'div#flash_notice'
|
assert_selector 'div#flash_notice'
|
||||||
assert_no_selector 'div#flash_error'
|
assert_no_selector 'div#flash_error'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
Reference in New Issue
Block a user