From 8bf38748d3140b7065b38f7d593483b9363d5ae5 Mon Sep 17 00:00:00 2001 From: JudeOchalifu Date: Thu, 11 Sep 2025 17:16:23 +0100 Subject: [PATCH 1/6] Added a simple login page --- templates/home.html | 73 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/templates/home.html b/templates/home.html index 657321f..90d1f66 100644 --- a/templates/home.html +++ b/templates/home.html @@ -1,9 +1,74 @@ - + + Login + + -

Hello Flask

-

{{ result }}

+
+

Login

+
+
+ + +
+
+ + +
+ +
+
- From 6690220615533a021e0f4c7f22b24ef0c04eb06c Mon Sep 17 00:00:00 2001 From: JudeOchalifu Date: Thu, 11 Sep 2025 17:25:08 +0100 Subject: [PATCH 2/6] Commented out jinja2 code Commented out the Jinja 2 code to prevent the syntax errors that were occurring when trying to run the Python code --- app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 22cee72..c18741a 100644 --- a/app.py +++ b/app.py @@ -85,8 +85,8 @@ def result(): except Exception as e: print(e) # print(len(constraints)) - {{ ingredient_db[selected_ingredients[i]]["Price"] }} - {% for i in range( 0, lengthOfIngredients): %} + # {{ ingredient_db[selected_ingredients[i]]["Price"] }} + # {% for i in range( 0, lengthOfIngredients): %} # for name in ingredient_names: From 7135f36c54cdce61c7933978256a77b4d57e158b Mon Sep 17 00:00:00 2001 From: JudeOchalifu Date: Thu, 11 Sep 2025 17:59:24 +0100 Subject: [PATCH 3/6] Added login navigation --- app.py | 4 ++-- templates/feed_form.html | 3 +-- templates/home.html | 8 +++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index c18741a..91f948d 100644 --- a/app.py +++ b/app.py @@ -85,8 +85,8 @@ def result(): except Exception as e: print(e) # print(len(constraints)) - # {{ ingredient_db[selected_ingredients[i]]["Price"] }} - # {% for i in range( 0, lengthOfIngredients): %} + #{{ ingredient_db[selected_ingredients[i]]["Price"] }} + #{% for i in range( 0, lengthOfIngredients): %} # for name in ingredient_names: diff --git a/templates/feed_form.html b/templates/feed_form.html index 85f11f9..5fdee15 100644 --- a/templates/feed_form.html +++ b/templates/feed_form.html @@ -117,5 +117,4 @@

Select Ingredients

} - -g \ No newline at end of file + \ No newline at end of file diff --git a/templates/home.html b/templates/home.html index 90d1f66..a095bbc 100644 --- a/templates/home.html +++ b/templates/home.html @@ -67,8 +67,14 @@

Login

- + + From f13e3b276d8b258e6c82e96b5171e692c5c3461f Mon Sep 17 00:00:00 2001 From: JudeOchalifu Date: Thu, 11 Sep 2025 19:35:51 +0100 Subject: [PATCH 4/6] Added validation to login form --- templates/home.html | 99 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/templates/home.html b/templates/home.html index a095bbc..abdbd58 100644 --- a/templates/home.html +++ b/templates/home.html @@ -72,9 +72,98 @@

Login

+ var submitBtn = document.querySelector('#submitButton'); + var usernameInput = document.querySelector('#username'); + var passwordInput = document.querySelector('#password'); + + submitBtn.addEventListener('click', function(e) { + e.preventDefault(); // Prevent form submission + + // Clear previous error messages + clearErrorMessages(); + + // Get form values + var username = usernameInput.value.trim(); + var password = passwordInput.value.trim(); + + // Validation flags + var isValid = true; + + // Username validation + if (username === '') { + showError('username', 'Username is required'); + isValid = false; + } else if (username.length < 3) { + showError('username', 'Username must be at least 3 characters'); + isValid = false; + } + + // Password validation + if (password === '') { + showError('password', 'Password is required'); + isValid = false; + } else if (password.length < 6) { + showError('password', 'Password must be at least 6 characters'); + isValid = false; + } + + // If validation passes, redirect to form + if (isValid) { + // Show success message briefly + showSuccess('Login successful! Redirecting...'); + setTimeout(function() { + window.location.href = "http://localhost:5000/form"; + }, 1000); + } + }); + + function showError(fieldId, message) { + var field = document.getElementById(fieldId); + var errorDiv = document.createElement('div'); + errorDiv.className = 'error-message'; + errorDiv.textContent = message; + errorDiv.style.color = '#dc3545'; + errorDiv.style.fontSize = '14px'; + errorDiv.style.marginTop = '5px'; + + // Add red border to input + field.style.borderColor = '#dc3545'; + + // Insert error message after the input + field.parentNode.insertBefore(errorDiv, field.nextSibling); + } + + function showSuccess(message) { + var successDiv = document.createElement('div'); + successDiv.className = 'success-message'; + successDiv.textContent = message; + successDiv.style.color = '#28a745'; + successDiv.style.fontSize = '16px'; + successDiv.style.textAlign = 'center'; + successDiv.style.marginTop = '10px'; + successDiv.style.fontWeight = 'bold'; + + // Insert success message after the form + var form = document.querySelector('form'); + form.parentNode.insertBefore(successDiv, form.nextSibling); + } + + function clearErrorMessages() { + // Remove existing error messages + var errorMessages = document.querySelectorAll('.error-message'); + errorMessages.forEach(function(msg) { + msg.remove(); + }); + + // Remove existing success messages + var successMessages = document.querySelectorAll('.success-message'); + successMessages.forEach(function(msg) { + msg.remove(); + }); + + // Reset input borders + usernameInput.style.borderColor = '#ddd'; + passwordInput.style.borderColor = '#ddd'; + } + From 99b00a4296d705403dee911ecd69a5fa6a941456 Mon Sep 17 00:00:00 2001 From: JudeOchalifu Date: Thu, 11 Sep 2025 20:02:04 +0100 Subject: [PATCH 5/6] Upgraded UI for a more professional look --- app.py | 160 ++++++++++--------- main.py | 12 +- templates/feed_form.html | 306 +++++++++++++++++++++++++++-------- templates/home.html | 17 +- templates/result.html | 335 +++++++++++++++++++++++++++++---------- 5 files changed, 580 insertions(+), 250 deletions(-) diff --git a/app.py b/app.py index 91f948d..a00775c 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, request, jsonify, json +from flask import Flask, render_template, request, jsonify, json, redirect, url_for from optlang import Model, Variable, Constraint, Objective from formulator_service import INGREDIENT_DB from formulator_service import INGREDIENT_PRICE @@ -31,91 +31,93 @@ def result(): # print(ration) # Computation starts here--- + + # animal ration(nutrient requirement) + animal_ration = ANIMAL_FEED_REQUIREMENT_DB[animal_type] - # animal ration(nutrient requirement) - animal_ration = ANIMAL_FEED_REQUIREMENT_DB[animal_type] - - # Define variables - # variables = {} - # variable_object = {} - # for i in range(1, len(ingredient_names)+1): - # variable_object[ingredient_names[i-1]] = 'x'+str(i) - # for ration in animal_ration: - # variables[ration] = {} - # for k, v in variable_object.items(): - # for name in ingredient_names: - # var = Variable(v, lb=0) - # variables[ration][name] = var - variables = {} - - for ration in animal_ration: - variables[ration] = {} - for name in ingredient_names: - var = Variable("{}".format(name), lb=0) - variables[ration][name] = var - print(variables) - print(len(variables)) + # Define variables + # variables = {} + # variable_object = {} + # for i in range(1, len(ingredient_names)+1): + # variable_object[ingredient_names[i-1]] = 'x'+str(i) + # for ration in animal_ration: + # variables[ration] = {} + # for k, v in variable_object.items(): + # for name in ingredient_names: + # var = Variable(v, lb=0) + # variables[ration][name] = var + variables = {} + + for ration in animal_ration: + variables[ration] = {} + for name in ingredient_names: + var = Variable("{}".format(name), lb=0) + variables[ration][name] = var + print(variables) + print(len(variables)) - # Get nutrient level of feed ingredients - # for name in ingredient_names: - # for ration in animal_ration: - # # if (INGREDIENT_DB[name] != ration): - # # a.append(animal_ration[ration]) - # # else: - # try: - # a.append(INGREDIENT_DB[name][ration]) - # except Exception as e: - # print(e) - # print(a) + # Get nutrient level of feed ingredients + # for name in ingredient_names: + # for ration in animal_ration: + # # if (INGREDIENT_DB[name] != ration): + # # a.append(animal_ration[ration]) + # # else: + # try: + # a.append(INGREDIENT_DB[name][ration]) + # except Exception as e: + # print(e) + # print(a) - # Define constraints - constraints = [] + # Define constraints + constraints = [] - for ration in animal_ration: - try: - const = Constraint( - sum((INGREDIENT_DB[name][ration]/100) * variables[name][ration] - if ration in INGREDIENT_DB[name] - else animal_ration[ration] * variables[name][ration] - for name in ingredient_names - ), - lb=animal_ration[ration] - ) - constraints.append(const) - except Exception as e: - print(e) - # print(len(constraints)) - #{{ ingredient_db[selected_ingredients[i]]["Price"] }} - #{% for i in range( 0, lengthOfIngredients): %} + for ration in animal_ration: + try: + const = Constraint( + sum((INGREDIENT_DB[name][ration]/100) * variables[name][ration] + if ration in INGREDIENT_DB[name] + else animal_ration[ration] * variables[name][ration] + for name in ingredient_names + ), + lb=animal_ration[ration] + ) + constraints.append(const) + except Exception as e: + print(e) + # print(len(constraints)) + #{{ ingredient_db[selected_ingredients[i]]["Price"] }} + #{% for i in range( 0, lengthOfIngredients): %} - # for name in ingredient_names: - # print(name) - # print("-" * 10) - # for k, v in variable_object.items(): - # print(v) + # for name in ingredient_names: + # print(name) + # print("-" * 10) + # for k, v in variable_object.items(): + # print(v) - # Objective function - for ration in animal_ration: - obj = Objective( - sum(INGREDIENT_PRICE[name] * variables[name][ration] for name in ingredient_names), - direction='min' - ) - # Objective( 58*x1+150*x2+60*x3+15*x4+50*x5+90*x6+700*x7+1300*x8+550*x9) + # Objective function + for ration in animal_ration: + obj = Objective( + sum(INGREDIENT_PRICE[name] * variables[name][ration] for name in ingredient_names), + direction='min' + ) + # Objective( 58*x1+150*x2+60*x3+15*x4+50*x5+90*x6+700*x7+1300*x8+550*x9) - # print(obj) - # Solve - model = Model() - model.objective = obj - model.add(constraints) - status = model.optimize() - print("status:", status) - print("objective value:", model.objective.value) - print("-------------") - for var_name , var in model.variables.items(): - print(var_name, "=", var.primal) - # result = model.objective.value - - return render_template("result.html", animal_type = animal_type) + # print(obj) + # Solve + model = Model() + model.objective = obj + model.add(constraints) + status = model.optimize() + print("status:", status) + print("objective value:", model.objective.value) + print("-------------") + for var_name , var in model.variables.items(): + print(var_name, "=", var.primal) + # result = model.objective.value + + return render_template("result.html", animal_type = animal_type) + + return redirect(url_for('form')) app.run(debug=True) diff --git a/main.py b/main.py index 05241a7..961bbf5 100644 --- a/main.py +++ b/main.py @@ -110,20 +110,16 @@ def result(): # print("=",animal_db[animal_selected][selected_animal_stage][nutrient][bound]) if bound == "Min": - contraints_list.append(Constraint(temp_var_sum, lb= - animal_db[animal_selected][selected_animal_stage][nutrient][bound])) + contraints_list.append(Constraint(temp_var_sum, lb=animal_db[animal_selected][selected_animal_stage][nutrient][bound])) print(temp_var_sum, ">=", animal_db[animal_selected][selected_animal_stage][nutrient][bound]) elif bound == "Max": - contraints_list.append(Constraint(temp_var_sum, ub= - animal_db[animal_selected][selected_animal_stage][nutrient][bound])) + contraints_list.append(Constraint(temp_var_sum, ub=animal_db[animal_selected][selected_animal_stage][nutrient][bound])) print(temp_var_sum, "<=", animal_db[animal_selected][selected_animal_stage][nutrient][bound]) elif bound == "Equal": - contraints_list.append(Constraint(temp_var_sum, lb= - animal_db[animal_selected][selected_animal_stage][nutrient][bound])) - contraints_list.append(Constraint(temp_var_sum, ub= - animal_db[animal_selected][selected_animal_stage][nutrient][bound])) + contraints_list.append(Constraint(temp_var_sum, lb=animal_db[animal_selected][selected_animal_stage][nutrient][bound])) + contraints_list.append(Constraint(temp_var_sum, ub=animal_db[animal_selected][selected_animal_stage][nutrient][bound])) print(temp_var_sum, ">=", animal_db[animal_selected][selected_animal_stage][nutrient][bound]) print(temp_var_sum, "<=", animal_db[animal_selected][selected_animal_stage][nutrient][bound]) diff --git a/templates/feed_form.html b/templates/feed_form.html index 5fdee15..c22c175 100644 --- a/templates/feed_form.html +++ b/templates/feed_form.html @@ -1,99 +1,275 @@ - + + + + Feed Formulation Software + - - -
- - | - - - | - - - - -

-
- -
- -
+
+
+

Feed Formulator

+

Optimize your animal feed formulation with precision

+
+ +
+ +
+

Animal Selection

+
+
+ + +
+
+ + +
+
+ + +
+
+
-
-
- Maize
- Soybean meal
- Oyster shell
- Bone meal
- Table salt
- Lysine
- Methionine
- Wheat Bran
- Broiler Premix
+
+

Select Ingredients

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+
-

- - - + + From aa895141c9212d14ff144577373a39cbfd33e6d8 Mon Sep 17 00:00:00 2001 From: JudeOchalifu Date: Thu, 11 Sep 2025 20:08:39 +0100 Subject: [PATCH 6/6] Added "Select All" for ingredients selection --- templates/feed_form.html | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/templates/feed_form.html b/templates/feed_form.html index c22c175..8850855 100644 --- a/templates/feed_form.html +++ b/templates/feed_form.html @@ -105,6 +105,37 @@ box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1); } + .select-all-container { + margin-bottom: 20px; + padding: 15px; + background: #e8f5e8; + border-radius: 10px; + border: 2px solid #4CAF50; + } + + .select-all-item { + display: flex; + align-items: center; + padding: 10px; + background: white; + border-radius: 8px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + } + + .select-all-item input[type="checkbox"] { + margin-right: 12px; + width: 20px; + height: 20px; + accent-color: #4CAF50; + } + + .select-all-label { + font-weight: 600; + color: #2c3e50; + cursor: pointer; + font-size: 1.1rem; + } + .ingredients-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); @@ -223,6 +254,12 @@

Animal Selection

Select Ingredients

+
+
+ + +
+
@@ -291,6 +328,39 @@

Select Ingredients

}); } } + + function toggleAllIngredients() { + const selectAllCheckbox = document.getElementById('selectAll'); + const ingredientCheckboxes = document.querySelectorAll('.ingredient-item input[type="checkbox"]'); + + ingredientCheckboxes.forEach(checkbox => { + checkbox.checked = selectAllCheckbox.checked; + }); + } + + // Add event listeners to individual checkboxes to update "Select All" state + document.addEventListener('DOMContentLoaded', function() { + const selectAllCheckbox = document.getElementById('selectAll'); + const ingredientCheckboxes = document.querySelectorAll('.ingredient-item input[type="checkbox"]'); + + ingredientCheckboxes.forEach(checkbox => { + checkbox.addEventListener('change', function() { + const allChecked = Array.from(ingredientCheckboxes).every(cb => cb.checked); + const someChecked = Array.from(ingredientCheckboxes).some(cb => cb.checked); + + if (allChecked) { + selectAllCheckbox.checked = true; + selectAllCheckbox.indeterminate = false; + } else if (someChecked) { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = true; + } else { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = false; + } + }); + }); + }); \ No newline at end of file