Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add formula relations to calculators constructor #1006

48 changes: 48 additions & 0 deletions app/assets/stylesheets/pages/calculator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ select.custom-select {
margin: 0;
}
}
.result-wrapper {
display: flex;
width: fit-content;
}
.vector-width {
width: 100px;
.vector {
Expand All @@ -279,6 +283,21 @@ select.custom-select {
.vector-mobile {
display: none;
}
.vector-mobile-left {
display: none;
}
.vector-mobile-right {
display: none;
}
.right {
padding-left: 30px;
padding-top: 85px;
transform: translateX(-40px);
}
.left {
padding-bottom: 85px;
transform: rotate(180deg) translateX(40px);
}
}
}

Expand Down Expand Up @@ -347,6 +366,12 @@ select.custom-select {
align-items: center;
max-width: 879px;

.result-wrapper {
display: block;
width: 350px;
margin-right: 140px;
margin-top: 0;
}
.vector-width {
width: 350px;
.vector {
Expand All @@ -357,6 +382,25 @@ select.custom-select {
display: block;
margin: auto;
}
.vector-mobile-left {
display: block;
margin: auto;
transform: rotate(180deg);
}
.vector-mobile-right {
display: block;
margin: auto;
}
.right {
display: none;
padding-top: 25px;
transform: none;
}
.left {
display: none;
padding-top: 25px;
transform: none;
}
}
}
}
Expand Down Expand Up @@ -389,6 +433,10 @@ select.custom-select {
.vector-width {
width: 300px;
}
.result-wrapper {
width: 280px;
margin-right: 0;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/account/calculators_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def collect_fields_for_kind(kind)
def calculator_params
params.require(:calculator).permit(
:id, :en_name, :uk_name, :color,
formulas_attributes: [:id, :expression, :en_label, :uk_label, :calculator_id, :en_unit, :uk_unit, :priority, :_destroy],
formulas_attributes: [:id, :expression, :en_label, :uk_label, :calculator_id, :en_unit, :uk_unit, :priority, :relation, :_destroy],
fields_attributes: [:id, :en_label, :uk_label, :var_name, :kind, :_destroy,
categories_attributes: [:id, :en_name, :uk_name, :price, :_destroy]]
)
Expand Down
2 changes: 2 additions & 0 deletions app/models/calculator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class Calculator < ApplicationRecord
has_many :fields, dependent: :destroy
has_many :formulas, -> { ordered_by_priority }, dependent: :destroy, inverse_of: :calculator

validates_with RelationValidator

accepts_nested_attributes_for :fields, allow_destroy: true
accepts_nested_attributes_for :formulas, allow_destroy: true

Expand Down
8 changes: 6 additions & 2 deletions app/services/calculators/calculation_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ def initialize(calculator, inputs)
def perform
@calculator.formulas.map do |formula|
result = @dentaku.evaluate(formula.expression, @inputs).round(2)

{ label: formula.label, result: result, unit: formula.unit }
{
label: formula.label,
result: result,
unit: formula.unit,
relation: formula.relation
}
end
end
end
40 changes: 40 additions & 0 deletions app/validators/relation_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class RelationValidator < ActiveModel::Validator
def validate(record)
relation_is_correct(record)
end

private

def relation_is_correct(record)
return if record.formulas.blank?

if record.formulas.first.relation == "previous"
record.formulas.first.errors.add(:relation, I18n.t("activerecord.errors.models.formula.attributes.expression.first_relation_error"))
end

if record.formulas.last.relation == "next"
record.formulas.last.errors.add(:relation, I18n.t("activerecord.errors.models.formula.attributes.expression.last_relation_error"))
end

last_relation = nil
last_formula_has_relation = false

record.formulas.each do |formula|
if last_relation == "next" && formula.relation == "previous"
formula.errors.add(:relation, I18n.t("activerecord.errors.models.formula.attributes.expression.relation_between_two_error"))
end

if last_formula_has_relation && formula.relation == "previous" || last_relation == "next" && formula.relation == "next"
formula.errors.add(:relation, I18n.t("activerecord.errors.models.formula.attributes.expression.already_have_relation_error"))
end

last_formula_has_relation = formula.relation.present? || last_relation == "next"

last_relation = formula.relation
end

if record.formulas.any? { |formula| formula.errors.present? }
record.errors.add(:formulas, "contain invalid relations.")
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

<%= f.input :uk_unit, label: "Uk Unit Label:" %>
<%= f.input :en_unit, label: "Unit Label:" %>
<%= f.input :relation, as: :select, label: "Relation of formula (optional):",
collection: { "None": nil, "Next": "next", "Previous": "previous"}, default: "None" %>
loqimean marked this conversation as resolved.
Show resolved Hide resolved

<%= link_to_remove_association "- Remove Formula", f, class: "text-red-500 underline" %>
</div>
Expand Down
12 changes: 11 additions & 1 deletion app/views/calculators/partials/_calculation_results.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
<h2 class="pt-6 text-2xl font-semibold text-center dynamic-text-color">Calculation Results</h2>
<div class="jumbotron calculation-results result main-result-container result-container">
<% results.each do |result| %>
<div>
<div class="result-wrapper">
<% if result[:relation] == "previous" %>
<%= render "calculators/partials/show/relation_arrow", locals: {rotate_direction: "left"} %>
<% end %>

<div>
<%= image_tag "#{formula_image}", class: "img-margin", alt: "icon" %>
<p class="dynamic-text-color">
<%= result[:result] %>
Expand All @@ -16,6 +21,11 @@
<p class="diapers-font-text">
<%= result[:label] %>
</p>
</div>

<% if result[:relation] == "next" %>
<%= render "calculators/partials/show/relation_arrow", locals: {rotate_direction: "right"} %>
<% end %>
</div>
<% end %>
</div>
Expand Down
4 changes: 4 additions & 0 deletions app/views/calculators/partials/show/_relation_arrow.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div class="vector-width">
<%= image_tag "icons/vector_5.png", class: locals[:rotate_direction], alt: "horizontal arrow" %>
<%= image_tag "icons/vector_2.png", class: "vector-mobile-#{locals[:rotate_direction]}", alt: "vertical arrow" %>
loqimean marked this conversation as resolved.
Show resolved Hide resolved
</div>
4 changes: 4 additions & 0 deletions config/locales/en/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,10 @@ en:
expression:
uninitialized_fields: "has uninitialized fields %{fields}"
mathematically_invalid: "is mathematically invalid"
first_relation_error: "is invalid no previous formula"
last_relation_error: "is invalid no next formula"
relation_between_two_error: "is invalid this formula already has a relation to the previous one"
already_have_relation_error: "is invalid formula can have relation with only one other formula"
form_errors:
one: "One error prohibited %{model} from being saved"
other: "%{count} errors prohibited this %{model} from being saved"
Expand Down
4 changes: 4 additions & 0 deletions config/locales/uk/uk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,10 @@ uk:
one: "має не ініціалізоване поле %{fields}"
other: "має не ініціалізовані поля %{fields}"
mathematically_invalid: "є математично некоректним"
first_relation_error: "некоректний немає попередньої формули"
last_relation_error: "некоректний немає наступної формули"
relation_between_two_error: "некориктний ця формула вже має зв'язок з попередньою"
already_have_relation_error: "формула може мати зв'язок лише з однією іншою формулою"
calculator:
attributes:
name:
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20241209084523_add_relation_to_formula.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddRelationToFormula < ActiveRecord::Migration[7.2]
def change
add_column :formulas, :relation, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.2].define(version: 2024_12_01_141708) do
ActiveRecord::Schema[7.2].define(version: 2024_12_09_084523) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
Expand Down Expand Up @@ -134,6 +134,7 @@
t.bigint "calculator_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "relation"
t.integer "priority", default: 0, null: false
t.index ["calculator_id"], name: "index_formulas_on_calculator_id"
end
Expand Down
10 changes: 5 additions & 5 deletions spec/helpers/calculators/calculation_service_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
let(:calculator) { instance_double("Calculator", formulas: formulas) }
let(:formulas) do
[
Formula.new(en_label: "Addition", en_unit: "units", uk_label: "Додавання", uk_unit: "одиниці", expression: "x + y"),
Formula.new(en_label: "Addition", en_unit: "units", uk_label: "Додавання", uk_unit: "одиниці", expression: "x + y", relation: "next"),
loqimean marked this conversation as resolved.
Show resolved Hide resolved
Formula.new(en_label: "Multiplication", en_unit: "units", uk_label: "Множення", uk_unit: "одиниці", expression: "x * y")
]
end
Expand All @@ -28,8 +28,8 @@

it "returns results with English labels and units" do
expect(subject).to eq([
{ label: "Addition", result: 8, unit: "units" },
{ label: "Multiplication", result: 15, unit: "units" }
{ label: "Addition", result: 8, unit: "units", relation: "next" },
{ label: "Multiplication", result: 15, unit: "units", relation: nil }
])
end
end
Expand All @@ -39,8 +39,8 @@

it "returns results with Ukrainian labels and units" do
expect(subject).to eq([
{ label: "Додавання", result: 8, unit: "одиниці" },
{ label: "Множення", result: 15, unit: "одиниці" }
{ label: "Додавання", result: 8, unit: "одиниці", relation: "next" },
{ label: "Множення", result: 15, unit: "одиниці", relation: nil }
])
end
end
Expand Down
67 changes: 67 additions & 0 deletions spec/validators/relation_validator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require "rails_helper"

RSpec.describe RelationValidator do
let(:calculator) { build(:calculator) }

describe "validations" do
let(:formula_previous_relation) { build(:formula, relation: "previous") }
let(:formula_next_relation) { build(:formula, relation: "next") }
let(:formula_none_relation) { build(:formula, relation: nil) }

context "when the first formula has a relation of previous" do
before do
calculator.formulas = [formula_previous_relation]
calculator.valid?
end

it "adds an error to the first formula" do
expect(formula_previous_relation.errors[:relation]).to include(I18n.t("activerecord.errors.models.formula.attributes.expression.first_relation_error"))
end
end

context "when the last formula has a relation of next" do
before do
calculator.formulas = [formula_none_relation, formula_next_relation]
calculator.valid?
end

it "adds an error to the last formula" do
expect(formula_next_relation.errors[:relation]).to include(I18n.t("activerecord.errors.models.formula.attributes.expression.last_relation_error"))
end
end

context "when formulas have consecutive relations 'next' and 'previous'" do
before do
calculator.formulas = [formula_next_relation, formula_previous_relation]
calculator.valid?
end

it "adds an error to the second formula" do
expect(formula_previous_relation.errors[:relation]).to include(I18n.t("activerecord.errors.models.formula.attributes.expression.relation_between_two_error"))
end
end

context "when fomula already have relation" do
before do
calculator.formulas = [formula_next_relation, formula_none_relation, formula_previous_relation]
calculator.valid?
end

it "adds an error to the second formula" do
expect(formula_previous_relation.errors[:relation]).to include(I18n.t("activerecord.errors.models.formula.attributes.expression.already_have_relation_error"))
end
end

context "when all formulas have valid relations" do
before do
calculator.formulas = [formula_next_relation, formula_none_relation, formula_next_relation, formula_none_relation]
calculator.valid?
end

it "does not add errors to the formulas" do
expect(formula_next_relation.errors[:relation]).to be_empty
expect(formula_none_relation.errors[:relation]).to be_empty
end
end
end
end
Loading