Emmanuel Frimpong Asante
commited on
Commit
·
6513666
1
Parent(s):
4a66159
"Update system"
Browse filesSigned-off-by: Emmanuel Frimpong Asante <[email protected]>
- .env +27 -0
- .idea/Poultry_Diseases_Detector.iml +1 -1
- .idea/misc.xml +1 -1
- app.py +52 -164
- auth/auth_controller.py +137 -0
- auth/auth_routes.py +72 -0
- auth/schemas/user_schema.py +109 -0
- config/db.py +46 -0
- config/settings.py +35 -0
- controllers/data_logging.py +115 -0
- controllers/health_management.py +93 -0
- controllers/inventory.py +120 -0
- controllers/usage_tracking.py +106 -0
- data/logs/daily_logs.py +157 -0
- data/logs/user_usage.py +131 -0
- models/schemas/health_schema.py +138 -0
- models/schemas/inventory_schema.py +146 -0
- models/schemas/log_schema.py +129 -0
- models/schemas/usage_schema.py +121 -0
- requirements.txt +25 -8
- routes/api.py +160 -0
- routes/data_logging_routes.py +157 -0
- routes/health_routes.py +56 -0
- routes/inventory_routes.py +77 -0
- routes/usage_routes.py +73 -0
- services/disease_detection.py +35 -0
- services/gradio_interface.py +34 -0
- services/inventory_manager.py +111 -0
- services/llama_service.py +28 -0
- services/report_generator.py +145 -0
- services/usage_tracker.py +118 -0
- static/assets/css/gpt.css +302 -0
- static/assets/{css/style.css → images/__init__.py} +0 -0
- static/assets/images/chatbot.jpg +3 -0
- static/assets/js/app.js +0 -0
- static/assets/js/gpt.js +140 -0
- templates/index.html +33 -0
.env
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# MongoDB Settings
|
2 |
+
MONGO_URI=mongodb://localhost:27017
|
3 |
+
DB_NAME=poultry_farm
|
4 |
+
|
5 |
+
# JWT Settings
|
6 |
+
SECRET_KEY=super_secret_key_here
|
7 |
+
JWT_ALGORITHM=HS256
|
8 |
+
JWT_EXPIRATION_HOURS=24
|
9 |
+
|
10 |
+
# Email Settings (optional)
|
11 |
+
EMAIL_HOST=smtp.your-email-provider.com
|
12 |
+
EMAIL_PORT=587
|
13 |
+
EMAIL_USE_TLS=True
|
14 | |
15 |
+
EMAIL_HOST_PASSWORD=your_secure_password
|
16 |
+
|
17 |
+
# Log Level
|
18 |
+
LOG_LEVEL=DEBUG
|
19 |
+
|
20 |
+
# External API Key (optional)
|
21 |
+
EXTERNAL_API_KEY=your_external_api_key_here
|
22 |
+
|
23 |
+
# Upload Folder Path
|
24 |
+
UPLOAD_FOLDER=/path/to/uploads
|
25 |
+
|
26 |
+
# Pagination
|
27 |
+
PAGINATION_LIMIT=10
|
.idea/Poultry_Diseases_Detector.iml
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
<content url="file://$MODULE_DIR$">
|
5 |
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
6 |
</content>
|
7 |
-
<orderEntry type="
|
8 |
<orderEntry type="sourceFolder" forTests="false" />
|
9 |
</component>
|
10 |
<component name="PackageRequirementsSettings">
|
|
|
4 |
<content url="file://$MODULE_DIR$">
|
5 |
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
6 |
</content>
|
7 |
+
<orderEntry type="jdk" jdkName="Poultry_Diseases_Detector (2)" jdkType="Python SDK" />
|
8 |
<orderEntry type="sourceFolder" forTests="false" />
|
9 |
</component>
|
10 |
<component name="PackageRequirementsSettings">
|
.idea/misc.xml
CHANGED
@@ -3,5 +3,5 @@
|
|
3 |
<component name="Black">
|
4 |
<option name="sdkName" value="Python 3.11 (Poultry_Diseases_Detector)" />
|
5 |
</component>
|
6 |
-
<component name="ProjectRootManager" version="2" project-jdk-name="
|
7 |
</project>
|
|
|
3 |
<component name="Black">
|
4 |
<option name="sdkName" value="Python 3.11 (Poultry_Diseases_Detector)" />
|
5 |
</component>
|
6 |
+
<component name="ProjectRootManager" version="2" project-jdk-name="Poultry_Diseases_Detector (2)" project-jdk-type="Python SDK" />
|
7 |
</project>
|
app.py
CHANGED
@@ -1,12 +1,26 @@
|
|
1 |
import os
|
2 |
import tensorflow as tf
|
3 |
-
from keras.models import load_model
|
4 |
-
import gradio as gr
|
5 |
-
import cv2
|
6 |
-
import numpy as np
|
7 |
-
from huggingface_hub import login
|
8 |
from pymongo import MongoClient
|
9 |
-
from
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
# Ensure the Hugging Face token is set
|
12 |
tok = os.getenv('HF_Token')
|
@@ -15,7 +29,7 @@ if tok:
|
|
15 |
else:
|
16 |
print("Warning: Hugging Face token not found in environment variables.")
|
17 |
|
18 |
-
# MongoDB Setup
|
19 |
MONGO_URI = os.getenv("MONGO_URI")
|
20 |
client = MongoClient(MONGO_URI)
|
21 |
db = client.poultry_farm # Database
|
@@ -34,161 +48,35 @@ if len(tf.config.list_physical_devices('GPU')) > 0:
|
|
34 |
else:
|
35 |
print("Using CPU without mixed precision")
|
36 |
|
37 |
-
#
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
#
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
#
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
except Exception as e:
|
68 |
-
print(f"Error in image preprocessing: {e}")
|
69 |
-
return None
|
70 |
-
|
71 |
-
# Predict Disease from Fecal Image
|
72 |
-
def predict(self, image):
|
73 |
-
image_check = self.preprocess_image(image)
|
74 |
-
if image_check is None:
|
75 |
-
return "Image preprocessing failed.", None, None, None
|
76 |
-
|
77 |
-
# Predict using the fecal disease detection model
|
78 |
-
indx = my_model.predict(image_check).argmax()
|
79 |
-
name = name_disease.get(indx, "Unknown disease")
|
80 |
-
status = result.get(indx, "unknown condition")
|
81 |
-
recom = recommend.get(indx, "no recommendation available")
|
82 |
-
|
83 |
-
# Generate additional information about the disease using Llama 2
|
84 |
-
detailed_response = self.generate_disease_response(name, status, recom)
|
85 |
-
return detailed_response, name, status, recom
|
86 |
-
|
87 |
-
# Generate a detailed response using Llama 2 for disease information and recommendations
|
88 |
-
def generate_disease_response(self, disease_name, status, recommendation):
|
89 |
-
prompt = (
|
90 |
-
f"The disease detected is {disease_name}, classified as {status}. "
|
91 |
-
f"Recommended action: {recommendation}. "
|
92 |
-
f"Here is some information about {disease_name}: causes, symptoms, and treatment methods "
|
93 |
-
"to effectively manage this condition on a poultry farm."
|
94 |
-
)
|
95 |
-
response = llama2_response(prompt)
|
96 |
-
# Post-process to remove the prompt if accidentally included in the response
|
97 |
-
return response.replace(prompt, "").strip()
|
98 |
-
|
99 |
-
# Diagnose Disease Using Fecal Image
|
100 |
-
def diagnose_disease(self, image):
|
101 |
-
if image is not None and image.size > 0: # Ensure the image is valid and has elements
|
102 |
-
return self.predict(image)
|
103 |
-
return "Please provide an image of poultry faecal matter for disease detection.", None, None, None
|
104 |
-
|
105 |
-
# Initialize the bot instance
|
106 |
-
bot = PoultryFarmBot()
|
107 |
-
|
108 |
-
# Load Llama 3.2 model and tokenizer for text generation
|
109 |
-
model_name = "meta-llama/Llama-3.2-1B"
|
110 |
-
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
111 |
-
model = AutoModelForCausalLM.from_pretrained(model_name)
|
112 |
-
|
113 |
-
# Set the padding token to EOS token or add a new padding token
|
114 |
-
if tokenizer.pad_token is None:
|
115 |
-
tokenizer.add_special_tokens({'pad_token': '[PAD]'})
|
116 |
-
model.resize_token_embeddings(len(tokenizer))
|
117 |
-
|
118 |
-
# Define Llama 2 response generation
|
119 |
-
def llama2_response(user_input):
|
120 |
-
try:
|
121 |
-
inputs = tokenizer(user_input, return_tensors="pt", truncation=True, max_length=150, padding=True)
|
122 |
-
outputs = model.generate(
|
123 |
-
inputs["input_ids"],
|
124 |
-
max_length=150,
|
125 |
-
do_sample=True,
|
126 |
-
temperature=0.7,
|
127 |
-
pad_token_id=tokenizer.pad_token_id, # Use the newly set padding token
|
128 |
-
attention_mask=inputs["attention_mask"]
|
129 |
-
)
|
130 |
-
|
131 |
-
# Decode and return the response
|
132 |
-
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
133 |
-
return response
|
134 |
-
except Exception as e:
|
135 |
-
return f"Error generating response: {str(e)}"
|
136 |
-
|
137 |
-
# Main chatbot function: handles both generative AI and disease detection
|
138 |
-
def chatbot_response(image, text):
|
139 |
-
# If an image is provided, perform disease detection
|
140 |
-
if image is not None:
|
141 |
-
diagnosis, name, status, recom = bot.diagnose_disease(image)
|
142 |
-
if name and status and recom:
|
143 |
-
return diagnosis
|
144 |
-
else:
|
145 |
-
return diagnosis # Return only the diagnostic message if no disease found
|
146 |
-
else:
|
147 |
-
# Use Llama 2 for more accurate responses
|
148 |
-
return llama2_response(text)
|
149 |
-
|
150 |
-
# Gradio interface styling and layout with ChatGPT-like theme
|
151 |
-
with gr.Blocks(theme=gr.themes.Soft(primary_hue="green", neutral_hue="slate")) as chatbot_interface:
|
152 |
-
gr.Markdown("# 🐔 Poultry Management Chatbot")
|
153 |
-
gr.Markdown(
|
154 |
-
"This chatbot can help you manage your poultry with conversational AI. Upload an image of poultry fecal matter for disease detection or just ask questions!"
|
155 |
-
)
|
156 |
-
|
157 |
-
with gr.Row():
|
158 |
-
with gr.Column(scale=1):
|
159 |
-
fecal_image = gr.Image(
|
160 |
-
label="Upload Image of Poultry Feces (Optional)",
|
161 |
-
type="numpy",
|
162 |
-
elem_id="image-upload",
|
163 |
-
show_label=True,
|
164 |
-
)
|
165 |
-
with gr.Column(scale=2):
|
166 |
-
user_input = gr.Textbox(
|
167 |
-
label="Type your question or chat with the assistant",
|
168 |
-
placeholder="Ask a question about poultry management...",
|
169 |
-
lines=3,
|
170 |
-
elem_id="user-input",
|
171 |
-
)
|
172 |
-
|
173 |
-
output_box = gr.Textbox(
|
174 |
-
label="Response",
|
175 |
-
placeholder="The response will appear here...",
|
176 |
-
interactive=False,
|
177 |
-
lines=10,
|
178 |
-
elem_id="output-box",
|
179 |
-
)
|
180 |
-
|
181 |
-
submit_button = gr.Button(
|
182 |
-
"Submit",
|
183 |
-
variant="primary",
|
184 |
-
elem_id="submit-button"
|
185 |
-
)
|
186 |
-
submit_button.click(
|
187 |
-
fn=chatbot_response,
|
188 |
-
inputs=[fecal_image, user_input],
|
189 |
-
outputs=[output_box]
|
190 |
-
)
|
191 |
-
|
192 |
-
# Launch the Gradio interface
|
193 |
if __name__ == "__main__":
|
194 |
-
|
|
|
1 |
import os
|
2 |
import tensorflow as tf
|
|
|
|
|
|
|
|
|
|
|
3 |
from pymongo import MongoClient
|
4 |
+
from flask import Flask, request, jsonify, render_template
|
5 |
+
from huggingface_hub import login
|
6 |
+
import threading
|
7 |
+
from services.disease_detection import PoultryFarmBot
|
8 |
+
from services.llama_service import llama2_response
|
9 |
+
from config.db import db
|
10 |
+
|
11 |
+
# Initialize Flask application
|
12 |
+
app = Flask(__name__, template_folder="templates", static_folder="static")
|
13 |
+
|
14 |
+
# Register Blueprints for API routes
|
15 |
+
from auth.auth_routes import auth_bp
|
16 |
+
from routes.health_routes import health_bp
|
17 |
+
from routes.inventory_routes import inventory_bp
|
18 |
+
from routes.usage_routes import usage_bp
|
19 |
+
|
20 |
+
app.register_blueprint(usage_bp, url_prefix='/api/usage')
|
21 |
+
app.register_blueprint(inventory_bp, url_prefix='/api/inventory')
|
22 |
+
app.register_blueprint(health_bp, url_prefix='/api/health')
|
23 |
+
app.register_blueprint(auth_bp, url_prefix='/auth')
|
24 |
|
25 |
# Ensure the Hugging Face token is set
|
26 |
tok = os.getenv('HF_Token')
|
|
|
29 |
else:
|
30 |
print("Warning: Hugging Face token not found in environment variables.")
|
31 |
|
32 |
+
# MongoDB Setup
|
33 |
MONGO_URI = os.getenv("MONGO_URI")
|
34 |
client = MongoClient(MONGO_URI)
|
35 |
db = client.poultry_farm # Database
|
|
|
48 |
else:
|
49 |
print("Using CPU without mixed precision")
|
50 |
|
51 |
+
# Initialize PoultryFarmBot
|
52 |
+
bot = PoultryFarmBot(db=db)
|
53 |
+
|
54 |
+
|
55 |
+
# Routes
|
56 |
+
@app.route('/')
|
57 |
+
def index():
|
58 |
+
return render_template('index.html')
|
59 |
+
|
60 |
+
|
61 |
+
# API to handle disease detection and AI assistant text response
|
62 |
+
@app.route('/api/chat', methods=['POST'])
|
63 |
+
def chat():
|
64 |
+
data = request.json
|
65 |
+
message = data.get('message')
|
66 |
+
image = data.get('image')
|
67 |
+
|
68 |
+
if image:
|
69 |
+
# Handle disease detection with image
|
70 |
+
diagnosis, name, status, recom = bot.predict(image)
|
71 |
+
return jsonify({
|
72 |
+
"response": f"Disease: {name}, Status: {status}, Recommendation: {recom}"
|
73 |
+
})
|
74 |
+
|
75 |
+
# Handle AI text generation with Llama model
|
76 |
+
response = llama2_response(message)
|
77 |
+
return jsonify({"response": response})
|
78 |
+
|
79 |
+
|
80 |
+
# Run the Flask app
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
if __name__ == "__main__":
|
82 |
+
app.run(debug=True)
|
auth/auth_controller.py
CHANGED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import bcrypt
|
2 |
+
import jwt
|
3 |
+
import datetime
|
4 |
+
from pymongo import MongoClient
|
5 |
+
from bson.objectid import ObjectId
|
6 |
+
from flask import jsonify, request
|
7 |
+
from functools import wraps
|
8 |
+
from config.db import db
|
9 |
+
|
10 |
+
# MongoDB setup (this should be moved to config/db.py and imported here)
|
11 |
+
client = MongoClient("your_mongo_uri") # Replace with your MongoDB URI
|
12 |
+
db = client.poultry_farm # Your database name
|
13 |
+
users_collection = db.users # Collection for storing user information
|
14 |
+
|
15 |
+
# Secret key for JWT (move to config/settings.py and import here)
|
16 |
+
SECRET_KEY = "your_secret_key" # Replace with a secure secret key
|
17 |
+
|
18 |
+
|
19 |
+
# JWT decorator to protect routes
|
20 |
+
def token_required(f):
|
21 |
+
@wraps(f)
|
22 |
+
def decorated(*args, **kwargs):
|
23 |
+
token = None
|
24 |
+
if 'x-access-token' in request.headers:
|
25 |
+
token = request.headers['x-access-token']
|
26 |
+
if not token:
|
27 |
+
return jsonify({'message': 'Token is missing!'}), 401
|
28 |
+
try:
|
29 |
+
data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
|
30 |
+
current_user = users_collection.find_one({"_id": ObjectId(data["user_id"])})
|
31 |
+
except:
|
32 |
+
return jsonify({'message': 'Token is invalid!'}), 401
|
33 |
+
return f(current_user, *args, **kwargs)
|
34 |
+
|
35 |
+
return decorated
|
36 |
+
|
37 |
+
|
38 |
+
# Register a new user
|
39 |
+
def register():
|
40 |
+
data = request.get_json()
|
41 |
+
if not data or not data.get('username') or not data.get('password'):
|
42 |
+
return jsonify({"message": "Missing username or password"}), 400
|
43 |
+
|
44 |
+
# Check if user already exists
|
45 |
+
if users_collection.find_one({"username": data['username']}):
|
46 |
+
return jsonify({"message": "User already exists"}), 400
|
47 |
+
|
48 |
+
# Hash the password
|
49 |
+
hashed_password = bcrypt.hashpw(data['password'].encode('utf-8'), bcrypt.gensalt())
|
50 |
+
|
51 |
+
# Create user document
|
52 |
+
new_user = {
|
53 |
+
"username": data['username'],
|
54 |
+
"password": hashed_password,
|
55 |
+
"role": data.get('role', 'user'), # Default role is 'user', could be 'manager'
|
56 |
+
"created_at": datetime.datetime.utcnow()
|
57 |
+
}
|
58 |
+
|
59 |
+
# Insert user into MongoDB
|
60 |
+
result = users_collection.insert_one(new_user)
|
61 |
+
return jsonify({"message": "User registered successfully", "user_id": str(result.inserted_id)}), 201
|
62 |
+
|
63 |
+
|
64 |
+
# User login
|
65 |
+
def login():
|
66 |
+
data = request.get_json()
|
67 |
+
if not data or not data.get('username') or not data.get('password'):
|
68 |
+
return jsonify({"message": "Missing username or password"}), 400
|
69 |
+
|
70 |
+
# Find user in MongoDB
|
71 |
+
user = users_collection.find_one({"username": data['username']})
|
72 |
+
if not user:
|
73 |
+
return jsonify({"message": "User not found"}), 404
|
74 |
+
|
75 |
+
# Verify password
|
76 |
+
if bcrypt.checkpw(data['password'].encode('utf-8'), user['password']):
|
77 |
+
# Create JWT token
|
78 |
+
token = jwt.encode({
|
79 |
+
'user_id': str(user['_id']),
|
80 |
+
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=24)
|
81 |
+
}, SECRET_KEY, algorithm="HS256")
|
82 |
+
|
83 |
+
return jsonify({"token": token}), 200
|
84 |
+
else:
|
85 |
+
return jsonify({"message": "Invalid password"}), 401
|
86 |
+
|
87 |
+
|
88 |
+
# Get user info (example of a protected route)
|
89 |
+
@token_required
|
90 |
+
def get_user_info(current_user):
|
91 |
+
user_info = {
|
92 |
+
"username": current_user['username'],
|
93 |
+
"role": current_user['role'],
|
94 |
+
"created_at": current_user['created_at']
|
95 |
+
}
|
96 |
+
return jsonify(user_info), 200
|
97 |
+
|
98 |
+
|
99 |
+
# Update user info (for manager/admin use)
|
100 |
+
@token_required
|
101 |
+
def update_user_info(current_user):
|
102 |
+
if current_user['role'] != 'manager': # Only managers can update user info
|
103 |
+
return jsonify({"message": "Permission denied"}), 403
|
104 |
+
|
105 |
+
data = request.get_json()
|
106 |
+
if not data or not data.get('user_id'):
|
107 |
+
return jsonify({"message": "Missing user_id"}), 400
|
108 |
+
|
109 |
+
user = users_collection.find_one({"_id": ObjectId(data['user_id'])})
|
110 |
+
if not user:
|
111 |
+
return jsonify({"message": "User not found"}), 404
|
112 |
+
|
113 |
+
# Update user information (only certain fields)
|
114 |
+
update_fields = {}
|
115 |
+
if 'username' in data:
|
116 |
+
update_fields['username'] = data['username']
|
117 |
+
if 'role' in data:
|
118 |
+
update_fields['role'] = data['role']
|
119 |
+
|
120 |
+
users_collection.update_one({"_id": ObjectId(data['user_id'])}, {"$set": update_fields})
|
121 |
+
return jsonify({"message": "User information updated successfully"}), 200
|
122 |
+
|
123 |
+
|
124 |
+
# Delete a user (for manager/admin use)
|
125 |
+
@token_required
|
126 |
+
def delete_user(current_user):
|
127 |
+
if current_user['role'] != 'manager': # Only managers can delete users
|
128 |
+
return jsonify({"message": "Permission denied"}), 403
|
129 |
+
|
130 |
+
data = request.get_json()
|
131 |
+
if not data or not data.get('user_id'):
|
132 |
+
return jsonify({"message": "Missing user_id"}), 400
|
133 |
+
|
134 |
+
result = users_collection.delete_one({"_id": ObjectId(data['user_id'])})
|
135 |
+
if result.deleted_count == 0:
|
136 |
+
return jsonify({"message": "User not found"}), 404
|
137 |
+
return jsonify({"message": "User deleted successfully"}), 200
|
auth/auth_routes.py
CHANGED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Blueprint
|
2 |
+
from .auth_controller import register, login, get_user_info, update_user_info, delete_user
|
3 |
+
|
4 |
+
# Create a Blueprint for auth-related routes
|
5 |
+
auth_bp = Blueprint('auth', __name__)
|
6 |
+
|
7 |
+
|
8 |
+
# Define the routes
|
9 |
+
@auth_bp.route('/register', methods=['POST'])
|
10 |
+
def register_user():
|
11 |
+
"""
|
12 |
+
Route for registering a new user.
|
13 |
+
This expects a POST request with a JSON payload containing:
|
14 |
+
{
|
15 |
+
"username": "user1",
|
16 |
+
"password": "password123",
|
17 |
+
"role": "user" # Optional, default is "user"
|
18 |
+
}
|
19 |
+
"""
|
20 |
+
return register()
|
21 |
+
|
22 |
+
|
23 |
+
@auth_bp.route('/login', methods=['POST'])
|
24 |
+
def login_user():
|
25 |
+
"""
|
26 |
+
Route for logging in an existing user.
|
27 |
+
This expects a POST request with a JSON payload containing:
|
28 |
+
{
|
29 |
+
"username": "user1",
|
30 |
+
"password": "password123"
|
31 |
+
}
|
32 |
+
"""
|
33 |
+
return login()
|
34 |
+
|
35 |
+
|
36 |
+
@auth_bp.route('/user-info', methods=['GET'])
|
37 |
+
def get_user_details():
|
38 |
+
"""
|
39 |
+
Route for getting the authenticated user's information.
|
40 |
+
Requires the JWT token in the `x-access-token` header.
|
41 |
+
"""
|
42 |
+
return get_user_info()
|
43 |
+
|
44 |
+
|
45 |
+
@auth_bp.route('/update-user', methods=['PUT'])
|
46 |
+
def update_user():
|
47 |
+
"""
|
48 |
+
Route for updating a user's information.
|
49 |
+
Only accessible by managers.
|
50 |
+
Expects a PUT request with a JSON payload containing:
|
51 |
+
{
|
52 |
+
"user_id": "the user's MongoDB ObjectId",
|
53 |
+
"username": "newUsername" # Optional
|
54 |
+
"role": "manager" # Optional, can be "user" or "manager"
|
55 |
+
}
|
56 |
+
Requires the JWT token in the `x-access-token` header.
|
57 |
+
"""
|
58 |
+
return update_user_info()
|
59 |
+
|
60 |
+
|
61 |
+
@auth_bp.route('/delete-user', methods=['DELETE'])
|
62 |
+
def remove_user():
|
63 |
+
"""
|
64 |
+
Route for deleting a user from the system.
|
65 |
+
Only accessible by managers.
|
66 |
+
Expects a DELETE request with a JSON payload containing:
|
67 |
+
{
|
68 |
+
"user_id": "the user's MongoDB ObjectId"
|
69 |
+
}
|
70 |
+
Requires the JWT token in the `x-access-token` header.
|
71 |
+
"""
|
72 |
+
return delete_user()
|
auth/schemas/user_schema.py
CHANGED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
|
4 |
+
# MongoDB connection (move to config/db.py and import here if needed)
|
5 |
+
client = MongoClient("your_mongo_uri") # Replace with your MongoDB URI
|
6 |
+
db = client.poultry_farm # Your database name
|
7 |
+
users_collection = db.users # Collection for users
|
8 |
+
|
9 |
+
|
10 |
+
# Define the User Schema
|
11 |
+
class User:
|
12 |
+
def __init__(self, username, password, role="user"):
|
13 |
+
"""
|
14 |
+
Initializes a User object.
|
15 |
+
|
16 |
+
:param username: String, the username for the user
|
17 |
+
:param password: String, the hashed password for the user
|
18 |
+
:param role: String, user role (either 'user' or 'manager'), default is 'user'
|
19 |
+
"""
|
20 |
+
self.username = username
|
21 |
+
self.password = password
|
22 |
+
self.role = role
|
23 |
+
self.created_at = datetime.datetime.utcnow() # Store account creation time
|
24 |
+
|
25 |
+
def to_dict(self):
|
26 |
+
"""
|
27 |
+
Converts the User object to a dictionary format for MongoDB insertion.
|
28 |
+
:return: dict
|
29 |
+
"""
|
30 |
+
return {
|
31 |
+
"username": self.username,
|
32 |
+
"password": self.password, # Password should be hashed before storing
|
33 |
+
"role": self.role,
|
34 |
+
"created_at": self.created_at
|
35 |
+
}
|
36 |
+
|
37 |
+
|
38 |
+
# CRUD Operations for User Schema
|
39 |
+
|
40 |
+
def create_user(username, hashed_password, role="user"):
|
41 |
+
"""
|
42 |
+
Create a new user in the MongoDB collection.
|
43 |
+
|
44 |
+
:param username: String, the username for the user
|
45 |
+
:param hashed_password: String, the hashed password of the user
|
46 |
+
:param role: String, role of the user (either 'user' or 'manager'), default is 'user'
|
47 |
+
:return: Inserted user's ID (MongoDB ObjectId)
|
48 |
+
"""
|
49 |
+
user = User(username=username, password=hashed_password, role=role)
|
50 |
+
user_id = users_collection.insert_one(user.to_dict()).inserted_id
|
51 |
+
return str(user_id)
|
52 |
+
|
53 |
+
|
54 |
+
def get_user_by_username(username):
|
55 |
+
"""
|
56 |
+
Retrieve a user by their username.
|
57 |
+
|
58 |
+
:param username: String, the username of the user to retrieve
|
59 |
+
:return: User document if found, None if not
|
60 |
+
"""
|
61 |
+
return users_collection.find_one({"username": username})
|
62 |
+
|
63 |
+
|
64 |
+
def get_user_by_id(user_id):
|
65 |
+
"""
|
66 |
+
Retrieve a user by their MongoDB ObjectId.
|
67 |
+
|
68 |
+
:param user_id: String, the MongoDB ObjectId of the user
|
69 |
+
:return: User document if found, None if not
|
70 |
+
"""
|
71 |
+
return users_collection.find_one({"_id": ObjectId(user_id)})
|
72 |
+
|
73 |
+
|
74 |
+
def update_user(user_id, updated_fields):
|
75 |
+
"""
|
76 |
+
Update a user's information based on provided fields.
|
77 |
+
|
78 |
+
:param user_id: String, the MongoDB ObjectId of the user
|
79 |
+
:param updated_fields: dict, fields to update (e.g., username or role)
|
80 |
+
:return: Boolean, True if update was successful, False otherwise
|
81 |
+
"""
|
82 |
+
result = users_collection.update_one(
|
83 |
+
{"_id": ObjectId(user_id)},
|
84 |
+
{"$set": updated_fields}
|
85 |
+
)
|
86 |
+
return result.modified_count > 0
|
87 |
+
|
88 |
+
|
89 |
+
def delete_user(user_id):
|
90 |
+
"""
|
91 |
+
Delete a user from the MongoDB collection by their ObjectId.
|
92 |
+
|
93 |
+
:param user_id: String, the MongoDB ObjectId of the user
|
94 |
+
:return: Boolean, True if deletion was successful, False otherwise
|
95 |
+
"""
|
96 |
+
result = users_collection.delete_one({"_id": ObjectId(user_id)})
|
97 |
+
return result.deleted_count > 0
|
98 |
+
|
99 |
+
|
100 |
+
# Example function to create a sample user
|
101 |
+
if __name__ == "__main__":
|
102 |
+
# Hash a sample password (this should be done with bcrypt before passing into this function)
|
103 |
+
sample_username = "sample_user"
|
104 |
+
sample_hashed_password = "hashed_password_here" # Use bcrypt to hash passwords
|
105 |
+
sample_role = "user"
|
106 |
+
|
107 |
+
# Insert a sample user into the database
|
108 |
+
user_id = create_user(sample_username, sample_hashed_password, sample_role)
|
109 |
+
print(f"User created with ID: {user_id}")
|
config/db.py
CHANGED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
import os
|
3 |
+
|
4 |
+
# Load environment variables (if using environment files like .env)
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
|
7 |
+
# Load the .env file (ensure that it's present in the project root)
|
8 |
+
load_dotenv()
|
9 |
+
|
10 |
+
# MongoDB URI (retrieved from environment variables)
|
11 |
+
MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017") # Default fallback URI for local MongoDB
|
12 |
+
DB_NAME = os.getenv("DB_NAME", "poultry_farm") # Default database name if not specified in environment
|
13 |
+
|
14 |
+
# Initialize MongoDB client
|
15 |
+
client = MongoClient(MONGO_URI)
|
16 |
+
|
17 |
+
# Access the database
|
18 |
+
db = client[DB_NAME]
|
19 |
+
|
20 |
+
# Collections (define and expose MongoDB collections to be used in the system)
|
21 |
+
users_collection = db.users # User collection
|
22 |
+
logs_collection = db.logs # Logs collection (e.g., health logs, usage logs)
|
23 |
+
inventory_collection = db.inventory # Inventory collection for feed, medicines, etc.
|
24 |
+
health_collection = db.health # Health collection for poultry health records
|
25 |
+
usage_collection = db.usage # Assistant usage tracking collection
|
26 |
+
|
27 |
+
# Test the database connection
|
28 |
+
try:
|
29 |
+
# Print a list of databases to ensure connection is successful
|
30 |
+
print("Databases in MongoDB:", client.list_database_names())
|
31 |
+
print(f"Connected to MongoDB database: {DB_NAME}")
|
32 |
+
except Exception as e:
|
33 |
+
print(f"Error connecting to MongoDB: {e}")
|
34 |
+
|
35 |
+
# Example usage of collections
|
36 |
+
if __name__ == "__main__":
|
37 |
+
# Insert a test document into the users collection
|
38 |
+
test_user = {
|
39 |
+
"username": "admin",
|
40 |
+
"password": "hashed_password", # In practice, you would hash the password using bcrypt
|
41 |
+
"role": "manager",
|
42 |
+
"created_at": datetime.datetime.utcnow()
|
43 |
+
}
|
44 |
+
|
45 |
+
result = users_collection.insert_one(test_user)
|
46 |
+
print(f"Inserted test user with ID: {result.inserted_id}")
|
config/settings.py
CHANGED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
|
4 |
+
# Load environment variables from .env file
|
5 |
+
load_dotenv()
|
6 |
+
|
7 |
+
# Secret Key for JWT encoding/decoding
|
8 |
+
SECRET_KEY = os.getenv("SECRET_KEY", "your_default_secret_key") # Replace with a strong default or pull from .env
|
9 |
+
|
10 |
+
# MongoDB connection settings
|
11 |
+
MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017") # Default to local MongoDB if not set
|
12 |
+
DB_NAME = os.getenv("DB_NAME", "poultry_farm") # Default database name
|
13 |
+
|
14 |
+
# JWT Settings
|
15 |
+
JWT_ALGORITHM = os.getenv("JWT_ALGORITHM", "HS256") # Default to HS256 algorithm for JWT
|
16 |
+
JWT_EXPIRATION_HOURS = int(os.getenv("JWT_EXPIRATION_HOURS", 24)) # Default JWT token expiration is 24 hours
|
17 |
+
|
18 |
+
# Email settings (if email notifications are implemented)
|
19 |
+
EMAIL_HOST = os.getenv("EMAIL_HOST", "smtp.example.com")
|
20 |
+
EMAIL_PORT = int(os.getenv("EMAIL_PORT", 587))
|
21 |
+
EMAIL_USE_TLS = bool(os.getenv("EMAIL_USE_TLS", True))
|
22 |
+
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "[email protected]")
|
23 |
+
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "your_email_password")
|
24 |
+
|
25 |
+
# Logging settings (if any log levels or formats are configured)
|
26 |
+
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO") # Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL
|
27 |
+
|
28 |
+
# Example external APIs (if required for additional services)
|
29 |
+
EXTERNAL_API_KEY = os.getenv("EXTERNAL_API_KEY", "your_default_api_key")
|
30 |
+
|
31 |
+
# Path settings (if needed for file uploads or storage)
|
32 |
+
UPLOAD_FOLDER = os.getenv("UPLOAD_FOLDER", "/path/to/uploads")
|
33 |
+
|
34 |
+
# Define any other global settings here
|
35 |
+
PAGINATION_LIMIT = int(os.getenv("PAGINATION_LIMIT", 10)) # Default pagination limit for listing endpoints
|
controllers/data_logging.py
CHANGED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import request, jsonify
|
2 |
+
from models.schemas.log_schema import create_log
|
3 |
+
from models.schemas.health_schema import create_health_record
|
4 |
+
from models.schemas.inventory_schema import create_inventory_item, update_inventory_item
|
5 |
+
from models.schemas.usage_schema import create_usage_record
|
6 |
+
import datetime
|
7 |
+
|
8 |
+
|
9 |
+
# Log an action performed by the user (general logging)
|
10 |
+
def log_user_action(user_id, message, level="INFO", metadata=None):
|
11 |
+
"""
|
12 |
+
Logs a general user action.
|
13 |
+
|
14 |
+
:param user_id: The ID of the user performing the action.
|
15 |
+
:param message: Description of the action.
|
16 |
+
:param level: Log level (default is "INFO").
|
17 |
+
:param metadata: Additional information about the action.
|
18 |
+
:return: JSON response indicating success or failure.
|
19 |
+
"""
|
20 |
+
try:
|
21 |
+
ip_address = request.remote_addr # Get the IP address from the request
|
22 |
+
log_id = create_log(level, message, metadata, ip_address, user_id)
|
23 |
+
return jsonify({"message": "Action logged successfully", "log_id": log_id}), 201
|
24 |
+
except Exception as e:
|
25 |
+
return jsonify({"error": str(e)}), 500
|
26 |
+
|
27 |
+
|
28 |
+
# Log a health record for a poultry
|
29 |
+
def log_health_record(user_id, poultry_id, disease_name, status, recommendation, image_path=None):
|
30 |
+
"""
|
31 |
+
Logs a health record for a poultry bird.
|
32 |
+
|
33 |
+
:param user_id: The ID of the user managing the poultry.
|
34 |
+
:param poultry_id: The unique identifier for the poultry bird.
|
35 |
+
:param disease_name: The name of the detected disease.
|
36 |
+
:param status: The health status of the bird (e.g., "Critical", "No issue").
|
37 |
+
:param recommendation: The recommended treatment or action.
|
38 |
+
:param image_path: Optional. The path to an image related to the health record.
|
39 |
+
:return: JSON response indicating success or failure.
|
40 |
+
"""
|
41 |
+
try:
|
42 |
+
record_id = create_health_record(user_id, poultry_id, disease_name, status, recommendation, image_path)
|
43 |
+
# Log the action
|
44 |
+
log_message = f"Health record created for poultry ID {poultry_id} - Disease: {disease_name}"
|
45 |
+
log_user_action(user_id, log_message, "INFO")
|
46 |
+
return jsonify({"message": "Health record logged successfully", "record_id": record_id}), 201
|
47 |
+
except Exception as e:
|
48 |
+
return jsonify({"error": str(e)}), 500
|
49 |
+
|
50 |
+
|
51 |
+
# Log an inventory addition
|
52 |
+
def log_inventory_addition(user_id, item_name, category, quantity, unit, description=None):
|
53 |
+
"""
|
54 |
+
Logs an inventory addition (e.g., adding feed, medicine).
|
55 |
+
|
56 |
+
:param user_id: The ID of the user managing the inventory.
|
57 |
+
:param item_name: The name of the inventory item.
|
58 |
+
:param category: The category of the item (e.g., "Feed", "Medicine").
|
59 |
+
:param quantity: The quantity added.
|
60 |
+
:param unit: The unit of measurement (e.g., "kg", "liters").
|
61 |
+
:param description: Optional. Additional notes or description of the item.
|
62 |
+
:return: JSON response indicating success or failure.
|
63 |
+
"""
|
64 |
+
try:
|
65 |
+
item_id = create_inventory_item(item_name, category, quantity, unit, user_id, description=description)
|
66 |
+
# Log the action
|
67 |
+
log_message = f"Inventory item added: {item_name}, Quantity: {quantity} {unit}"
|
68 |
+
log_user_action(user_id, log_message, "INFO")
|
69 |
+
return jsonify({"message": "Inventory item logged successfully", "item_id": item_id}), 201
|
70 |
+
except Exception as e:
|
71 |
+
return jsonify({"error": str(e)}), 500
|
72 |
+
|
73 |
+
|
74 |
+
# Log an inventory update (e.g., usage or adjustments)
|
75 |
+
def log_inventory_update(user_id, item_id, updated_fields):
|
76 |
+
"""
|
77 |
+
Logs an update to an inventory item (e.g., when quantity is used).
|
78 |
+
|
79 |
+
:param user_id: The ID of the user managing the inventory.
|
80 |
+
:param item_id: The ID of the inventory item being updated.
|
81 |
+
:param updated_fields: The fields to be updated (e.g., "used_quantity", "quantity").
|
82 |
+
:return: JSON response indicating success or failure.
|
83 |
+
"""
|
84 |
+
try:
|
85 |
+
update_success = update_inventory_item(item_id, updated_fields)
|
86 |
+
if update_success:
|
87 |
+
# Log the action
|
88 |
+
log_message = f"Inventory item updated: {item_id} - {updated_fields}"
|
89 |
+
log_user_action(user_id, log_message, "INFO")
|
90 |
+
return jsonify({"message": "Inventory item updated successfully"}), 200
|
91 |
+
else:
|
92 |
+
return jsonify({"message": "Inventory item update failed"}), 400
|
93 |
+
except Exception as e:
|
94 |
+
return jsonify({"error": str(e)}), 500
|
95 |
+
|
96 |
+
|
97 |
+
# Log assistant usage
|
98 |
+
def log_assistant_usage(user_id, manager_id, interaction_type, metadata=None):
|
99 |
+
"""
|
100 |
+
Logs the usage of the assistant by a user.
|
101 |
+
|
102 |
+
:param user_id: The ID of the user interacting with the assistant.
|
103 |
+
:param manager_id: The ID of the manager overseeing the user.
|
104 |
+
:param interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
105 |
+
:param metadata: Optional. Additional details about the interaction.
|
106 |
+
:return: JSON response indicating success or failure.
|
107 |
+
"""
|
108 |
+
try:
|
109 |
+
usage_record_id = create_usage_record(user_id, manager_id, interaction_type, metadata)
|
110 |
+
# Log the action
|
111 |
+
log_message = f"Assistant interaction logged: {interaction_type} by user {user_id}"
|
112 |
+
log_user_action(user_id, log_message, "INFO", metadata)
|
113 |
+
return jsonify({"message": "Assistant usage logged successfully", "usage_record_id": usage_record_id}), 201
|
114 |
+
except Exception as e:
|
115 |
+
return jsonify({"error": str(e)}), 500
|
controllers/health_management.py
CHANGED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import request, jsonify
|
2 |
+
from models.schemas.health_schema import create_health_record, get_health_record_by_id, get_health_records_by_user
|
3 |
+
from services.disease_detection import detect_disease
|
4 |
+
from controllers.data_logging import log_health_record
|
5 |
+
|
6 |
+
|
7 |
+
# Detect Disease and Log Health Record
|
8 |
+
def detect_and_log_disease():
|
9 |
+
"""
|
10 |
+
Detect disease from the provided image and log the health record.
|
11 |
+
Expects a POST request with the following fields:
|
12 |
+
- user_id: ID of the user submitting the disease detection request.
|
13 |
+
- poultry_id: The ID of the poultry bird.
|
14 |
+
- image: Image of the poultry feces for disease detection (binary file).
|
15 |
+
|
16 |
+
Returns:
|
17 |
+
- JSON response with the disease detection result and health record logging status.
|
18 |
+
"""
|
19 |
+
try:
|
20 |
+
# Extract the data from the request
|
21 |
+
user_id = request.form.get('user_id')
|
22 |
+
poultry_id = request.form.get('poultry_id')
|
23 |
+
image = request.files.get('image')
|
24 |
+
|
25 |
+
if not user_id or not poultry_id or not image:
|
26 |
+
return jsonify({"error": "Missing required fields: user_id, poultry_id, or image"}), 400
|
27 |
+
|
28 |
+
# Run disease detection
|
29 |
+
disease_name, status, recommendation = detect_disease(image)
|
30 |
+
|
31 |
+
# Log the health record
|
32 |
+
record_id = log_health_record(
|
33 |
+
user_id=user_id,
|
34 |
+
poultry_id=poultry_id,
|
35 |
+
disease_name=disease_name,
|
36 |
+
status=status,
|
37 |
+
recommendation=recommendation
|
38 |
+
)
|
39 |
+
|
40 |
+
return jsonify({
|
41 |
+
"message": "Disease detected and health record logged successfully",
|
42 |
+
"disease_name": disease_name,
|
43 |
+
"status": status,
|
44 |
+
"recommendation": recommendation,
|
45 |
+
"record_id": record_id
|
46 |
+
}), 201
|
47 |
+
|
48 |
+
except Exception as e:
|
49 |
+
return jsonify({"error": str(e)}), 500
|
50 |
+
|
51 |
+
|
52 |
+
# Get Health Record by ID
|
53 |
+
def get_health_record(record_id):
|
54 |
+
"""
|
55 |
+
Retrieves a health record by its record_id (MongoDB ObjectId).
|
56 |
+
|
57 |
+
Expects:
|
58 |
+
- record_id: ID of the health record.
|
59 |
+
|
60 |
+
Returns:
|
61 |
+
- JSON response with the health record details or an error message if not found.
|
62 |
+
"""
|
63 |
+
try:
|
64 |
+
health_record = get_health_record_by_id(record_id)
|
65 |
+
if not health_record:
|
66 |
+
return jsonify({"message": "Health record not found"}), 404
|
67 |
+
|
68 |
+
return jsonify(health_record), 200
|
69 |
+
|
70 |
+
except Exception as e:
|
71 |
+
return jsonify({"error": str(e)}), 500
|
72 |
+
|
73 |
+
|
74 |
+
# Get Health Records by User
|
75 |
+
def get_health_records_for_user(user_id):
|
76 |
+
"""
|
77 |
+
Retrieves all health records created by a specific user.
|
78 |
+
|
79 |
+
Expects:
|
80 |
+
- user_id: ID of the user whose records are being requested.
|
81 |
+
|
82 |
+
Returns:
|
83 |
+
- JSON response with the list of health records for the user or an error message if not found.
|
84 |
+
"""
|
85 |
+
try:
|
86 |
+
health_records = get_health_records_by_user(user_id)
|
87 |
+
if not health_records:
|
88 |
+
return jsonify({"message": "No health records found for this user"}), 404
|
89 |
+
|
90 |
+
return jsonify(health_records), 200
|
91 |
+
|
92 |
+
except Exception as e:
|
93 |
+
return jsonify({"error": str(e)}), 500
|
controllers/inventory.py
CHANGED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import request, jsonify
|
2 |
+
from models.schemas.inventory_schema import create_inventory_item, update_inventory_item, get_inventory_item_by_id, \
|
3 |
+
get_all_inventory_items
|
4 |
+
from controllers.data_logging import log_inventory_addition, log_inventory_update
|
5 |
+
|
6 |
+
|
7 |
+
# Add a new inventory item
|
8 |
+
def add_inventory_item():
|
9 |
+
"""
|
10 |
+
Adds a new inventory item to the system.
|
11 |
+
|
12 |
+
Expects:
|
13 |
+
- user_id: The ID of the user adding the item.
|
14 |
+
- item_name: Name of the inventory item.
|
15 |
+
- category: Category of the item (e.g., "Feed", "Medicine").
|
16 |
+
- quantity: Total quantity of the item.
|
17 |
+
- unit: Unit of measurement (e.g., "kg", "liters").
|
18 |
+
- description: Optional. Additional notes or description about the item.
|
19 |
+
|
20 |
+
Returns:
|
21 |
+
- JSON response with the result of the addition.
|
22 |
+
"""
|
23 |
+
try:
|
24 |
+
# Extract data from the request
|
25 |
+
user_id = request.json.get('user_id')
|
26 |
+
item_name = request.json.get('item_name')
|
27 |
+
category = request.json.get('category')
|
28 |
+
quantity = request.json.get('quantity')
|
29 |
+
unit = request.json.get('unit')
|
30 |
+
description = request.json.get('description')
|
31 |
+
|
32 |
+
if not user_id or not item_name or not category or not quantity or not unit:
|
33 |
+
return jsonify({"error": "Missing required fields"}), 400
|
34 |
+
|
35 |
+
# Add the inventory item
|
36 |
+
item_id = create_inventory_item(item_name, category, quantity, unit, user_id, description)
|
37 |
+
|
38 |
+
# Log the inventory addition
|
39 |
+
log_inventory_addition(user_id, item_name, category, quantity, unit, description)
|
40 |
+
|
41 |
+
return jsonify({"message": "Inventory item added successfully", "item_id": item_id}), 201
|
42 |
+
|
43 |
+
except Exception as e:
|
44 |
+
return jsonify({"error": str(e)}), 500
|
45 |
+
|
46 |
+
|
47 |
+
# Update an existing inventory item
|
48 |
+
def update_inventory_item_by_id():
|
49 |
+
"""
|
50 |
+
Updates an existing inventory item.
|
51 |
+
|
52 |
+
Expects:
|
53 |
+
- user_id: The ID of the user updating the item.
|
54 |
+
- item_id: The ID of the inventory item to update.
|
55 |
+
- updated_fields: Dictionary containing fields to update (e.g., "quantity", "used_quantity").
|
56 |
+
|
57 |
+
Returns:
|
58 |
+
- JSON response with the result of the update.
|
59 |
+
"""
|
60 |
+
try:
|
61 |
+
# Extract data from the request
|
62 |
+
user_id = request.json.get('user_id')
|
63 |
+
item_id = request.json.get('item_id')
|
64 |
+
updated_fields = request.json.get('updated_fields')
|
65 |
+
|
66 |
+
if not user_id or not item_id or not updated_fields:
|
67 |
+
return jsonify({"error": "Missing required fields"}), 400
|
68 |
+
|
69 |
+
# Update the inventory item
|
70 |
+
update_success = update_inventory_item(item_id, updated_fields)
|
71 |
+
if update_success:
|
72 |
+
# Log the inventory update
|
73 |
+
log_inventory_update(user_id, item_id, updated_fields)
|
74 |
+
return jsonify({"message": "Inventory item updated successfully"}), 200
|
75 |
+
else:
|
76 |
+
return jsonify({"message": "Inventory item update failed"}), 400
|
77 |
+
|
78 |
+
except Exception as e:
|
79 |
+
return jsonify({"error": str(e)}), 500
|
80 |
+
|
81 |
+
|
82 |
+
# Get an inventory item by its ID
|
83 |
+
def get_inventory_by_id(item_id):
|
84 |
+
"""
|
85 |
+
Retrieves an inventory item by its item_id (MongoDB ObjectId).
|
86 |
+
|
87 |
+
Expects:
|
88 |
+
- item_id: ID of the inventory item.
|
89 |
+
|
90 |
+
Returns:
|
91 |
+
- JSON response with the inventory item details.
|
92 |
+
"""
|
93 |
+
try:
|
94 |
+
inventory_item = get_inventory_item_by_id(item_id)
|
95 |
+
if not inventory_item:
|
96 |
+
return jsonify({"message": "Inventory item not found"}), 404
|
97 |
+
|
98 |
+
return jsonify(inventory_item), 200
|
99 |
+
|
100 |
+
except Exception as e:
|
101 |
+
return jsonify({"error": str(e)}), 500
|
102 |
+
|
103 |
+
|
104 |
+
# Get all inventory items
|
105 |
+
def get_all_inventory():
|
106 |
+
"""
|
107 |
+
Retrieves all inventory items.
|
108 |
+
|
109 |
+
Returns:
|
110 |
+
- JSON response with the list of all inventory items.
|
111 |
+
"""
|
112 |
+
try:
|
113 |
+
inventory_items = get_all_inventory_items()
|
114 |
+
if not inventory_items:
|
115 |
+
return jsonify({"message": "No inventory items found"}), 404
|
116 |
+
|
117 |
+
return jsonify(inventory_items), 200
|
118 |
+
|
119 |
+
except Exception as e:
|
120 |
+
return jsonify({"error": str(e)}), 500
|
controllers/usage_tracking.py
CHANGED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import request, jsonify
|
2 |
+
from models.schemas.usage_schema import create_usage_record, get_usage_by_user, get_usage_by_manager, \
|
3 |
+
get_usage_by_interaction_type
|
4 |
+
from controllers.data_logging import log_assistant_usage
|
5 |
+
|
6 |
+
|
7 |
+
# Log assistant usage by a user
|
8 |
+
def log_usage():
|
9 |
+
"""
|
10 |
+
Logs the usage of the assistant by a user.
|
11 |
+
|
12 |
+
Expects:
|
13 |
+
- user_id: The ID of the user interacting with the assistant.
|
14 |
+
- manager_id: The ID of the user's manager.
|
15 |
+
- interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
16 |
+
- metadata: Optional. Additional details about the interaction.
|
17 |
+
|
18 |
+
Returns:
|
19 |
+
- JSON response with the result of the logging operation.
|
20 |
+
"""
|
21 |
+
try:
|
22 |
+
# Extract data from the request
|
23 |
+
user_id = request.json.get('user_id')
|
24 |
+
manager_id = request.json.get('manager_id')
|
25 |
+
interaction_type = request.json.get('interaction_type')
|
26 |
+
metadata = request.json.get('metadata')
|
27 |
+
|
28 |
+
if not user_id or not manager_id or not interaction_type:
|
29 |
+
return jsonify({"error": "Missing required fields"}), 400
|
30 |
+
|
31 |
+
# Log assistant usage
|
32 |
+
usage_record_id = create_usage_record(user_id, manager_id, interaction_type, metadata)
|
33 |
+
|
34 |
+
# Log the usage in the system log
|
35 |
+
log_assistant_usage(user_id, manager_id, interaction_type, metadata)
|
36 |
+
|
37 |
+
return jsonify({"message": "Assistant usage logged successfully", "usage_record_id": usage_record_id}), 201
|
38 |
+
|
39 |
+
except Exception as e:
|
40 |
+
return jsonify({"error": str(e)}), 500
|
41 |
+
|
42 |
+
|
43 |
+
# Get usage records by user
|
44 |
+
def get_usage_for_user(user_id):
|
45 |
+
"""
|
46 |
+
Retrieves all assistant usage records for a specific user.
|
47 |
+
|
48 |
+
Expects:
|
49 |
+
- user_id: The ID of the user.
|
50 |
+
|
51 |
+
Returns:
|
52 |
+
- JSON response with the list of usage records for the user.
|
53 |
+
"""
|
54 |
+
try:
|
55 |
+
usage_records = get_usage_by_user(user_id)
|
56 |
+
if not usage_records:
|
57 |
+
return jsonify({"message": "No usage records found for this user"}), 404
|
58 |
+
|
59 |
+
return jsonify(usage_records), 200
|
60 |
+
|
61 |
+
except Exception as e:
|
62 |
+
return jsonify({"error": str(e)}), 500
|
63 |
+
|
64 |
+
|
65 |
+
# Get usage records for all users managed by a specific manager
|
66 |
+
def get_usage_for_manager(manager_id):
|
67 |
+
"""
|
68 |
+
Retrieves all assistant usage records for users managed by a specific manager.
|
69 |
+
|
70 |
+
Expects:
|
71 |
+
- manager_id: The ID of the manager.
|
72 |
+
|
73 |
+
Returns:
|
74 |
+
- JSON response with the list of usage records for users under the manager.
|
75 |
+
"""
|
76 |
+
try:
|
77 |
+
usage_records = get_usage_by_manager(manager_id)
|
78 |
+
if not usage_records:
|
79 |
+
return jsonify({"message": "No usage records found for this manager's users"}), 404
|
80 |
+
|
81 |
+
return jsonify(usage_records), 200
|
82 |
+
|
83 |
+
except Exception as e:
|
84 |
+
return jsonify({"error": str(e)}), 500
|
85 |
+
|
86 |
+
|
87 |
+
# Get usage records by interaction type
|
88 |
+
def get_usage_by_type(interaction_type):
|
89 |
+
"""
|
90 |
+
Retrieves all assistant usage records for a specific interaction type.
|
91 |
+
|
92 |
+
Expects:
|
93 |
+
- interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
94 |
+
|
95 |
+
Returns:
|
96 |
+
- JSON response with the list of usage records for the specified interaction type.
|
97 |
+
"""
|
98 |
+
try:
|
99 |
+
usage_records = get_usage_by_interaction_type(interaction_type)
|
100 |
+
if not usage_records:
|
101 |
+
return jsonify({"message": f"No usage records found for interaction type '{interaction_type}'"}), 404
|
102 |
+
|
103 |
+
return jsonify(usage_records), 200
|
104 |
+
|
105 |
+
except Exception as e:
|
106 |
+
return jsonify({"error": str(e)}), 500
|
data/logs/daily_logs.py
CHANGED
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
import datetime
|
4 |
+
|
5 |
+
# MongoDB connection (import from your db configuration)
|
6 |
+
from config.db import db
|
7 |
+
|
8 |
+
# Define the daily logs collection
|
9 |
+
daily_logs_collection = db.daily_logs # Collection to store daily logs
|
10 |
+
|
11 |
+
|
12 |
+
# Daily Log Schema
|
13 |
+
class DailyLog:
|
14 |
+
def __init__(self, user_id, date, health_status, feed_consumption, mortality_rate, inventory_levels=None,
|
15 |
+
notes=None):
|
16 |
+
"""
|
17 |
+
Initializes a DailyLog object.
|
18 |
+
|
19 |
+
:param user_id: The ID of the user submitting the daily log.
|
20 |
+
:param date: The date of the log (in YYYY-MM-DD format).
|
21 |
+
:param health_status: A summary of the poultry health status.
|
22 |
+
:param feed_consumption: Total feed consumed (in kg, liters, etc.).
|
23 |
+
:param mortality_rate: The percentage or number of poultry that died.
|
24 |
+
:param inventory_levels: Optional. Current inventory levels of key items (feed, medicine).
|
25 |
+
:param notes: Optional. Additional notes or comments about the day's events.
|
26 |
+
"""
|
27 |
+
self.user_id = ObjectId(user_id)
|
28 |
+
self.date = date # Date format: "YYYY-MM-DD"
|
29 |
+
self.health_status = health_status
|
30 |
+
self.feed_consumption = feed_consumption
|
31 |
+
self.mortality_rate = mortality_rate
|
32 |
+
self.inventory_levels = inventory_levels if inventory_levels else {}
|
33 |
+
self.notes = notes
|
34 |
+
self.created_at = datetime.datetime.utcnow() # Timestamp when the log was created
|
35 |
+
|
36 |
+
def to_dict(self):
|
37 |
+
"""
|
38 |
+
Converts the DailyLog object to a dictionary format for MongoDB insertion.
|
39 |
+
:return: dict
|
40 |
+
"""
|
41 |
+
return {
|
42 |
+
"user_id": self.user_id,
|
43 |
+
"date": self.date,
|
44 |
+
"health_status": self.health_status,
|
45 |
+
"feed_consumption": self.feed_consumption,
|
46 |
+
"mortality_rate": self.mortality_rate,
|
47 |
+
"inventory_levels": self.inventory_levels,
|
48 |
+
"notes": self.notes,
|
49 |
+
"created_at": self.created_at
|
50 |
+
}
|
51 |
+
|
52 |
+
|
53 |
+
# CRUD Operations for Daily Logs
|
54 |
+
|
55 |
+
def create_daily_log(user_id, date, health_status, feed_consumption, mortality_rate, inventory_levels=None, notes=None):
|
56 |
+
"""
|
57 |
+
Creates a new daily log in the MongoDB collection.
|
58 |
+
|
59 |
+
:param user_id: The ID of the user submitting the log.
|
60 |
+
:param date: The date of the log.
|
61 |
+
:param health_status: Summary of the poultry health status.
|
62 |
+
:param feed_consumption: Total feed consumed.
|
63 |
+
:param mortality_rate: The percentage or number of poultry that died.
|
64 |
+
:param inventory_levels: Optional. Current inventory levels of key items (feed, medicine).
|
65 |
+
:param notes: Optional. Additional notes or comments about the day's events.
|
66 |
+
:return: The inserted log's ID (MongoDB ObjectId).
|
67 |
+
"""
|
68 |
+
daily_log = DailyLog(
|
69 |
+
user_id=user_id,
|
70 |
+
date=date,
|
71 |
+
health_status=health_status,
|
72 |
+
feed_consumption=feed_consumption,
|
73 |
+
mortality_rate=mortality_rate,
|
74 |
+
inventory_levels=inventory_levels,
|
75 |
+
notes=notes
|
76 |
+
)
|
77 |
+
log_id = daily_logs_collection.insert_one(daily_log.to_dict()).inserted_id
|
78 |
+
return str(log_id)
|
79 |
+
|
80 |
+
|
81 |
+
def get_daily_log_by_id(log_id):
|
82 |
+
"""
|
83 |
+
Retrieves a daily log by its MongoDB ObjectId.
|
84 |
+
|
85 |
+
:param log_id: The MongoDB ObjectId of the daily log.
|
86 |
+
:return: The daily log document if found, None if not.
|
87 |
+
"""
|
88 |
+
return daily_logs_collection.find_one({"_id": ObjectId(log_id)})
|
89 |
+
|
90 |
+
|
91 |
+
def get_daily_logs_by_user(user_id):
|
92 |
+
"""
|
93 |
+
Retrieves all daily logs submitted by a specific user.
|
94 |
+
|
95 |
+
:param user_id: The ID of the user.
|
96 |
+
:return: A list of daily log documents.
|
97 |
+
"""
|
98 |
+
return list(daily_logs_collection.find({"user_id": ObjectId(user_id)}))
|
99 |
+
|
100 |
+
|
101 |
+
def get_daily_logs_by_date(date):
|
102 |
+
"""
|
103 |
+
Retrieves all daily logs for a specific date.
|
104 |
+
|
105 |
+
:param date: The date of the logs (in YYYY-MM-DD format).
|
106 |
+
:return: A list of daily log documents for the specified date.
|
107 |
+
"""
|
108 |
+
return list(daily_logs_collection.find({"date": date}))
|
109 |
+
|
110 |
+
|
111 |
+
def update_daily_log(log_id, updated_fields):
|
112 |
+
"""
|
113 |
+
Updates a daily log's fields based on provided data.
|
114 |
+
|
115 |
+
:param log_id: The MongoDB ObjectId of the daily log.
|
116 |
+
:param updated_fields: A dictionary of fields to update (e.g., health_status, feed_consumption).
|
117 |
+
:return: Boolean, True if the update was successful, False otherwise.
|
118 |
+
"""
|
119 |
+
result = daily_logs_collection.update_one(
|
120 |
+
{"_id": ObjectId(log_id)},
|
121 |
+
{"$set": updated_fields}
|
122 |
+
)
|
123 |
+
return result.modified_count > 0
|
124 |
+
|
125 |
+
|
126 |
+
def delete_daily_log(log_id):
|
127 |
+
"""
|
128 |
+
Deletes a daily log from the MongoDB collection by its ObjectId.
|
129 |
+
|
130 |
+
:param log_id: The MongoDB ObjectId of the daily log.
|
131 |
+
:return: Boolean, True if the deletion was successful, False otherwise.
|
132 |
+
"""
|
133 |
+
result = daily_logs_collection.delete_one({"_id": ObjectId(log_id)})
|
134 |
+
return result.deleted_count > 0
|
135 |
+
|
136 |
+
|
137 |
+
# Example function to create a sample daily log
|
138 |
+
if __name__ == "__main__":
|
139 |
+
# Insert a test daily log
|
140 |
+
test_user_id = "610c5e8a85f0b2a4e0b8b918" # Replace with a valid user ObjectId
|
141 |
+
test_date = "2024-10-07"
|
142 |
+
test_health_status = "All poultry are healthy, no signs of disease."
|
143 |
+
test_feed_consumption = 500 # In kilograms
|
144 |
+
test_mortality_rate = 0.0 # No deaths today
|
145 |
+
test_inventory_levels = {"Feed": 1000, "Antibiotics": 50} # Inventory in kilograms and units
|
146 |
+
|
147 |
+
log_id = create_daily_log(
|
148 |
+
user_id=test_user_id,
|
149 |
+
date=test_date,
|
150 |
+
health_status=test_health_status,
|
151 |
+
feed_consumption=test_feed_consumption,
|
152 |
+
mortality_rate=test_mortality_rate,
|
153 |
+
inventory_levels=test_inventory_levels,
|
154 |
+
notes="Everything ran smoothly today."
|
155 |
+
)
|
156 |
+
|
157 |
+
print(f"Daily log created with ID: {log_id}")
|
data/logs/user_usage.py
CHANGED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
import datetime
|
4 |
+
|
5 |
+
# MongoDB connection (import from your db configuration)
|
6 |
+
from config.db import db
|
7 |
+
|
8 |
+
# Define the user usage logs collection
|
9 |
+
user_usage_collection = db.user_usage # Collection to store assistant usage logs
|
10 |
+
|
11 |
+
|
12 |
+
# User Usage Log Schema
|
13 |
+
class UserUsageLog:
|
14 |
+
def __init__(self, user_id, manager_id, interaction_type, metadata=None):
|
15 |
+
"""
|
16 |
+
Initializes a UserUsageLog object.
|
17 |
+
|
18 |
+
:param user_id: The ID of the user interacting with the assistant.
|
19 |
+
:param manager_id: The ID of the user's manager.
|
20 |
+
:param interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
21 |
+
:param metadata: Optional. Additional metadata related to the interaction (e.g., details of the query or result).
|
22 |
+
"""
|
23 |
+
self.user_id = ObjectId(user_id)
|
24 |
+
self.manager_id = ObjectId(manager_id)
|
25 |
+
self.interaction_type = interaction_type
|
26 |
+
self.metadata = metadata if metadata else {}
|
27 |
+
self.timestamp = datetime.datetime.utcnow() # Timestamp when the interaction occurred
|
28 |
+
|
29 |
+
def to_dict(self):
|
30 |
+
"""
|
31 |
+
Converts the UserUsageLog object to a dictionary format for MongoDB insertion.
|
32 |
+
:return: dict
|
33 |
+
"""
|
34 |
+
return {
|
35 |
+
"user_id": self.user_id,
|
36 |
+
"manager_id": self.manager_id,
|
37 |
+
"interaction_type": self.interaction_type,
|
38 |
+
"metadata": self.metadata,
|
39 |
+
"timestamp": self.timestamp
|
40 |
+
}
|
41 |
+
|
42 |
+
|
43 |
+
# CRUD Operations for User Usage Logs
|
44 |
+
|
45 |
+
def create_user_usage_log(user_id, manager_id, interaction_type, metadata=None):
|
46 |
+
"""
|
47 |
+
Creates a new user usage log in the MongoDB collection.
|
48 |
+
|
49 |
+
:param user_id: The ID of the user interacting with the assistant.
|
50 |
+
:param manager_id: The ID of the user's manager.
|
51 |
+
:param interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
52 |
+
:param metadata: Optional. Additional metadata related to the interaction.
|
53 |
+
:return: The inserted log's ID (MongoDB ObjectId).
|
54 |
+
"""
|
55 |
+
usage_log = UserUsageLog(
|
56 |
+
user_id=user_id,
|
57 |
+
manager_id=manager_id,
|
58 |
+
interaction_type=interaction_type,
|
59 |
+
metadata=metadata
|
60 |
+
)
|
61 |
+
log_id = user_usage_collection.insert_one(usage_log.to_dict()).inserted_id
|
62 |
+
return str(log_id)
|
63 |
+
|
64 |
+
|
65 |
+
def get_usage_log_by_id(log_id):
|
66 |
+
"""
|
67 |
+
Retrieves a user usage log by its MongoDB ObjectId.
|
68 |
+
|
69 |
+
:param log_id: The MongoDB ObjectId of the usage log.
|
70 |
+
:return: The usage log document if found, None if not.
|
71 |
+
"""
|
72 |
+
return user_usage_collection.find_one({"_id": ObjectId(log_id)})
|
73 |
+
|
74 |
+
|
75 |
+
def get_usage_logs_by_user(user_id):
|
76 |
+
"""
|
77 |
+
Retrieves all usage logs for a specific user.
|
78 |
+
|
79 |
+
:param user_id: The ID of the user.
|
80 |
+
:return: A list of usage log documents for the user.
|
81 |
+
"""
|
82 |
+
return list(user_usage_collection.find({"user_id": ObjectId(user_id)}))
|
83 |
+
|
84 |
+
|
85 |
+
def get_usage_logs_by_manager(manager_id):
|
86 |
+
"""
|
87 |
+
Retrieves all usage logs for users managed by a specific manager.
|
88 |
+
|
89 |
+
:param manager_id: The ID of the manager.
|
90 |
+
:return: A list of usage log documents for the users under the manager's supervision.
|
91 |
+
"""
|
92 |
+
return list(user_usage_collection.find({"manager_id": ObjectId(manager_id)}))
|
93 |
+
|
94 |
+
|
95 |
+
def get_usage_logs_by_interaction_type(interaction_type):
|
96 |
+
"""
|
97 |
+
Retrieves all usage logs for a specific interaction type (e.g., "disease_detection").
|
98 |
+
|
99 |
+
:param interaction_type: The type of interaction.
|
100 |
+
:return: A list of usage log documents for the specified interaction type.
|
101 |
+
"""
|
102 |
+
return list(user_usage_collection.find({"interaction_type": interaction_type}))
|
103 |
+
|
104 |
+
|
105 |
+
def delete_usage_log(log_id):
|
106 |
+
"""
|
107 |
+
Deletes a user usage log from the MongoDB collection by its ObjectId.
|
108 |
+
|
109 |
+
:param log_id: The MongoDB ObjectId of the usage log.
|
110 |
+
:return: Boolean, True if the deletion was successful, False otherwise.
|
111 |
+
"""
|
112 |
+
result = user_usage_collection.delete_one({"_id": ObjectId(log_id)})
|
113 |
+
return result.deleted_count > 0
|
114 |
+
|
115 |
+
|
116 |
+
# Example function to create a sample user usage log
|
117 |
+
if __name__ == "__main__":
|
118 |
+
# Insert a test user usage log
|
119 |
+
test_user_id = "610c5e8a85f0b2a4e0b8b918" # Replace with a valid user ObjectId
|
120 |
+
test_manager_id = "610c5e8a85f0b2a4e0b8b919" # Replace with a valid manager ObjectId
|
121 |
+
test_interaction_type = "disease_detection"
|
122 |
+
test_metadata = {"disease_name": "Coccidiosis", "detected": True}
|
123 |
+
|
124 |
+
log_id = create_user_usage_log(
|
125 |
+
user_id=test_user_id,
|
126 |
+
manager_id=test_manager_id,
|
127 |
+
interaction_type=test_interaction_type,
|
128 |
+
metadata=test_metadata
|
129 |
+
)
|
130 |
+
|
131 |
+
print(f"User usage log created with ID: {log_id}")
|
models/schemas/health_schema.py
CHANGED
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
import datetime
|
4 |
+
|
5 |
+
# MongoDB connection (move to config/db.py and import here)
|
6 |
+
from config.db import db
|
7 |
+
|
8 |
+
# Define the health collection
|
9 |
+
health_collection = db.health # Collection to store poultry health records
|
10 |
+
|
11 |
+
|
12 |
+
# Health Record Schema
|
13 |
+
class HealthRecord:
|
14 |
+
def __init__(self, user_id, poultry_id, disease_name, status, recommendation, image_path=None):
|
15 |
+
"""
|
16 |
+
Initializes a HealthRecord object.
|
17 |
+
|
18 |
+
:param user_id: The ID of the user who is managing the poultry.
|
19 |
+
:param poultry_id: A unique identifier for the poultry (e.g., tag number).
|
20 |
+
:param disease_name: The name of the detected disease (e.g., Coccidiosis, New Castle Disease).
|
21 |
+
:param status: The health status of the poultry (e.g., "Critical", "No issue").
|
22 |
+
:param recommendation: The treatment recommendation (e.g., "Percetamol", "Panadol").
|
23 |
+
:param image_path: Optional. The path to the image of the poultry or fecal matter.
|
24 |
+
"""
|
25 |
+
self.user_id = ObjectId(user_id) # Link to the user (farm manager)
|
26 |
+
self.poultry_id = poultry_id # Unique poultry ID (could be a tag, or system-generated)
|
27 |
+
self.disease_name = disease_name # Disease detected
|
28 |
+
self.status = status # Health status (e.g., "Critical", "No issue")
|
29 |
+
self.recommendation = recommendation # Suggested treatment or recommendation
|
30 |
+
self.image_path = image_path # Optional image path for the health record
|
31 |
+
self.created_at = datetime.datetime.utcnow() # Timestamp for record creation
|
32 |
+
|
33 |
+
def to_dict(self):
|
34 |
+
"""
|
35 |
+
Converts the HealthRecord object to a dictionary format for MongoDB insertion.
|
36 |
+
:return: dict
|
37 |
+
"""
|
38 |
+
return {
|
39 |
+
"user_id": self.user_id,
|
40 |
+
"poultry_id": self.poultry_id,
|
41 |
+
"disease_name": self.disease_name,
|
42 |
+
"status": self.status,
|
43 |
+
"recommendation": self.recommendation,
|
44 |
+
"image_path": self.image_path,
|
45 |
+
"created_at": self.created_at
|
46 |
+
}
|
47 |
+
|
48 |
+
|
49 |
+
# CRUD Operations for Health Records
|
50 |
+
|
51 |
+
def create_health_record(user_id, poultry_id, disease_name, status, recommendation, image_path=None):
|
52 |
+
"""
|
53 |
+
Creates a new health record in the MongoDB collection.
|
54 |
+
|
55 |
+
:param user_id: The ID of the user managing the poultry.
|
56 |
+
:param poultry_id: A unique identifier for the poultry.
|
57 |
+
:param disease_name: Name of the detected disease.
|
58 |
+
:param status: The health status of the poultry.
|
59 |
+
:param recommendation: Treatment recommendation.
|
60 |
+
:param image_path: Optional path to the related image.
|
61 |
+
:return: The inserted record's ID (MongoDB ObjectId).
|
62 |
+
"""
|
63 |
+
health_record = HealthRecord(
|
64 |
+
user_id=user_id,
|
65 |
+
poultry_id=poultry_id,
|
66 |
+
disease_name=disease_name,
|
67 |
+
status=status,
|
68 |
+
recommendation=recommendation,
|
69 |
+
image_path=image_path
|
70 |
+
)
|
71 |
+
record_id = health_collection.insert_one(health_record.to_dict()).inserted_id
|
72 |
+
return str(record_id)
|
73 |
+
|
74 |
+
|
75 |
+
def get_health_record_by_id(record_id):
|
76 |
+
"""
|
77 |
+
Retrieves a health record by its MongoDB ObjectId.
|
78 |
+
|
79 |
+
:param record_id: The MongoDB ObjectId of the health record.
|
80 |
+
:return: The health record document if found, None if not.
|
81 |
+
"""
|
82 |
+
return health_collection.find_one({"_id": ObjectId(record_id)})
|
83 |
+
|
84 |
+
|
85 |
+
def get_health_records_by_user(user_id):
|
86 |
+
"""
|
87 |
+
Retrieves all health records created by a specific user.
|
88 |
+
|
89 |
+
:param user_id: The ID of the user.
|
90 |
+
:return: A list of health record documents created by the user.
|
91 |
+
"""
|
92 |
+
return list(health_collection.find({"user_id": ObjectId(user_id)}))
|
93 |
+
|
94 |
+
|
95 |
+
def update_health_record(record_id, updated_fields):
|
96 |
+
"""
|
97 |
+
Updates a health record's fields based on provided data.
|
98 |
+
|
99 |
+
:param record_id: The MongoDB ObjectId of the health record.
|
100 |
+
:param updated_fields: A dictionary of fields to update (e.g., status, recommendation).
|
101 |
+
:return: Boolean, True if the update was successful, False otherwise.
|
102 |
+
"""
|
103 |
+
result = health_collection.update_one(
|
104 |
+
{"_id": ObjectId(record_id)},
|
105 |
+
{"$set": updated_fields}
|
106 |
+
)
|
107 |
+
return result.modified_count > 0
|
108 |
+
|
109 |
+
|
110 |
+
def delete_health_record(record_id):
|
111 |
+
"""
|
112 |
+
Deletes a health record from the MongoDB collection by its ObjectId.
|
113 |
+
|
114 |
+
:param record_id: The MongoDB ObjectId of the health record.
|
115 |
+
:return: Boolean, True if the deletion was successful, False otherwise.
|
116 |
+
"""
|
117 |
+
result = health_collection.delete_one({"_id": ObjectId(record_id)})
|
118 |
+
return result.deleted_count > 0
|
119 |
+
|
120 |
+
|
121 |
+
# Example function to create a sample health record
|
122 |
+
if __name__ == "__main__":
|
123 |
+
# Insert a test health record
|
124 |
+
test_user_id = "610c5e8a85f0b2a4e0b8b918" # Replace with a valid user ObjectId
|
125 |
+
test_poultry_id = "poultry123"
|
126 |
+
test_disease_name = "Coccidiosis"
|
127 |
+
test_status = "Critical"
|
128 |
+
test_recommendation = "Panadol"
|
129 |
+
|
130 |
+
record_id = create_health_record(
|
131 |
+
user_id=test_user_id,
|
132 |
+
poultry_id=test_poultry_id,
|
133 |
+
disease_name=test_disease_name,
|
134 |
+
status=test_status,
|
135 |
+
recommendation=test_recommendation
|
136 |
+
)
|
137 |
+
|
138 |
+
print(f"Health record created with ID: {record_id}")
|
models/schemas/inventory_schema.py
CHANGED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
import datetime
|
4 |
+
|
5 |
+
# MongoDB connection (move to config/db.py and import here)
|
6 |
+
from config.db import db
|
7 |
+
|
8 |
+
# Define the inventory collection
|
9 |
+
inventory_collection = db.inventory # Collection to store inventory items
|
10 |
+
|
11 |
+
|
12 |
+
# Inventory Item Schema
|
13 |
+
class InventoryItem:
|
14 |
+
def __init__(self, item_name, category, quantity, unit, added_by, used_quantity=0, description=None):
|
15 |
+
"""
|
16 |
+
Initializes an InventoryItem object.
|
17 |
+
|
18 |
+
:param item_name: The name of the inventory item (e.g., "Chicken Feed", "Antibiotics").
|
19 |
+
:param category: The category of the item (e.g., "Feed", "Medicine", "Supplies").
|
20 |
+
:param quantity: The total quantity of the item.
|
21 |
+
:param unit: The unit of measurement (e.g., "kg", "liters", "units").
|
22 |
+
:param added_by: The ID of the user who added the item.
|
23 |
+
:param used_quantity: The quantity that has been used (defaults to 0).
|
24 |
+
:param description: Optional. Additional description or notes about the item.
|
25 |
+
"""
|
26 |
+
self.item_name = item_name
|
27 |
+
self.category = category
|
28 |
+
self.quantity = quantity
|
29 |
+
self.unit = unit
|
30 |
+
self.added_by = ObjectId(added_by) # User who added the item
|
31 |
+
self.used_quantity = used_quantity
|
32 |
+
self.description = description
|
33 |
+
self.created_at = datetime.datetime.utcnow() # Timestamp when the item was added
|
34 |
+
self.updated_at = datetime.datetime.utcnow() # Timestamp when the item was last updated
|
35 |
+
|
36 |
+
def to_dict(self):
|
37 |
+
"""
|
38 |
+
Converts the InventoryItem object to a dictionary format for MongoDB insertion.
|
39 |
+
:return: dict
|
40 |
+
"""
|
41 |
+
return {
|
42 |
+
"item_name": self.item_name,
|
43 |
+
"category": self.category,
|
44 |
+
"quantity": self.quantity,
|
45 |
+
"unit": self.unit,
|
46 |
+
"added_by": self.added_by,
|
47 |
+
"used_quantity": self.used_quantity,
|
48 |
+
"description": self.description,
|
49 |
+
"created_at": self.created_at,
|
50 |
+
"updated_at": self.updated_at
|
51 |
+
}
|
52 |
+
|
53 |
+
|
54 |
+
# CRUD Operations for Inventory Items
|
55 |
+
|
56 |
+
def create_inventory_item(item_name, category, quantity, unit, added_by, used_quantity=0, description=None):
|
57 |
+
"""
|
58 |
+
Creates a new inventory item in the MongoDB collection.
|
59 |
+
|
60 |
+
:param item_name: Name of the inventory item.
|
61 |
+
:param category: Category of the item (e.g., "Feed", "Medicine").
|
62 |
+
:param quantity: Total quantity available.
|
63 |
+
:param unit: Unit of measurement (e.g., kg, liters, units).
|
64 |
+
:param added_by: ID of the user who added the item.
|
65 |
+
:param used_quantity: Quantity that has been used (default is 0).
|
66 |
+
:param description: Optional description or notes.
|
67 |
+
:return: The inserted record's ID (MongoDB ObjectId).
|
68 |
+
"""
|
69 |
+
inventory_item = InventoryItem(
|
70 |
+
item_name=item_name,
|
71 |
+
category=category,
|
72 |
+
quantity=quantity,
|
73 |
+
unit=unit,
|
74 |
+
added_by=added_by,
|
75 |
+
used_quantity=used_quantity,
|
76 |
+
description=description
|
77 |
+
)
|
78 |
+
item_id = inventory_collection.insert_one(inventory_item.to_dict()).inserted_id
|
79 |
+
return str(item_id)
|
80 |
+
|
81 |
+
|
82 |
+
def get_inventory_item_by_id(item_id):
|
83 |
+
"""
|
84 |
+
Retrieves an inventory item by its MongoDB ObjectId.
|
85 |
+
|
86 |
+
:param item_id: The MongoDB ObjectId of the inventory item.
|
87 |
+
:return: The inventory item document if found, None if not.
|
88 |
+
"""
|
89 |
+
return inventory_collection.find_one({"_id": ObjectId(item_id)})
|
90 |
+
|
91 |
+
|
92 |
+
def get_all_inventory_items():
|
93 |
+
"""
|
94 |
+
Retrieves all inventory items from the MongoDB collection.
|
95 |
+
|
96 |
+
:return: A list of inventory item documents.
|
97 |
+
"""
|
98 |
+
return list(inventory_collection.find())
|
99 |
+
|
100 |
+
|
101 |
+
def update_inventory_item(item_id, updated_fields):
|
102 |
+
"""
|
103 |
+
Updates an inventory item's fields based on provided data.
|
104 |
+
|
105 |
+
:param item_id: The MongoDB ObjectId of the inventory item.
|
106 |
+
:param updated_fields: A dictionary of fields to update (e.g., quantity, used_quantity).
|
107 |
+
:return: Boolean, True if the update was successful, False otherwise.
|
108 |
+
"""
|
109 |
+
updated_fields["updated_at"] = datetime.datetime.utcnow() # Update the timestamp
|
110 |
+
result = inventory_collection.update_one(
|
111 |
+
{"_id": ObjectId(item_id)},
|
112 |
+
{"$set": updated_fields}
|
113 |
+
)
|
114 |
+
return result.modified_count > 0
|
115 |
+
|
116 |
+
|
117 |
+
def delete_inventory_item(item_id):
|
118 |
+
"""
|
119 |
+
Deletes an inventory item from the MongoDB collection by its ObjectId.
|
120 |
+
|
121 |
+
:param item_id: The MongoDB ObjectId of the inventory item.
|
122 |
+
:return: Boolean, True if the deletion was successful, False otherwise.
|
123 |
+
"""
|
124 |
+
result = inventory_collection.delete_one({"_id": ObjectId(item_id)})
|
125 |
+
return result.deleted_count > 0
|
126 |
+
|
127 |
+
|
128 |
+
# Example function to create a sample inventory item
|
129 |
+
if __name__ == "__main__":
|
130 |
+
# Insert a test inventory item
|
131 |
+
test_item_name = "Chicken Feed"
|
132 |
+
test_category = "Feed"
|
133 |
+
test_quantity = 1000 # 1000 kg
|
134 |
+
test_unit = "kg"
|
135 |
+
test_added_by = "610c5e8a85f0b2a4e0b8b918" # Replace with a valid user ObjectId
|
136 |
+
|
137 |
+
item_id = create_inventory_item(
|
138 |
+
item_name=test_item_name,
|
139 |
+
category=test_category,
|
140 |
+
quantity=test_quantity,
|
141 |
+
unit=test_unit,
|
142 |
+
added_by=test_added_by,
|
143 |
+
description="Premium feed for broiler chickens"
|
144 |
+
)
|
145 |
+
|
146 |
+
print(f"Inventory item created with ID: {item_id}")
|
models/schemas/log_schema.py
CHANGED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient, IndexModel, ASCENDING
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
import datetime
|
4 |
+
|
5 |
+
# MongoDB connection (move to config/db.py and import here)
|
6 |
+
from config.db import db
|
7 |
+
|
8 |
+
# Define the logs collection
|
9 |
+
logs_collection = db.logs # Collection to store system logs
|
10 |
+
|
11 |
+
# TTL index to expire documents after 30 days
|
12 |
+
logs_collection.create_index([("timestamp", ASCENDING)], expireAfterSeconds=30 * 24 * 60 * 60)
|
13 |
+
|
14 |
+
|
15 |
+
# Log Record Schema
|
16 |
+
class LogRecord:
|
17 |
+
def __init__(self, level, message, metadata=None, ip_address=None, user_id=None):
|
18 |
+
"""
|
19 |
+
Initializes a LogRecord object.
|
20 |
+
|
21 |
+
:param level: Logging level (e.g., "INFO", "ERROR", "WARNING", "DEBUG").
|
22 |
+
:param message: The log message or description of the event.
|
23 |
+
:param metadata: Optional. Additional metadata such as request details or stack trace.
|
24 |
+
:param ip_address: Optional. The IP address from where the action originated.
|
25 |
+
:param user_id: Optional. The ID of the user associated with the action (if applicable).
|
26 |
+
"""
|
27 |
+
self.level = level
|
28 |
+
self.message = message
|
29 |
+
self.metadata = metadata if metadata else {}
|
30 |
+
self.ip_address = ip_address
|
31 |
+
self.user_id = ObjectId(user_id) if user_id else None
|
32 |
+
self.timestamp = datetime.datetime.utcnow() # Timestamp of the log event
|
33 |
+
|
34 |
+
def to_dict(self):
|
35 |
+
"""
|
36 |
+
Converts the LogRecord object to a dictionary format for MongoDB insertion.
|
37 |
+
:return: dict
|
38 |
+
"""
|
39 |
+
return {
|
40 |
+
"level": self.level,
|
41 |
+
"message": self.message,
|
42 |
+
"metadata": self.metadata,
|
43 |
+
"ip_address": self.ip_address,
|
44 |
+
"user_id": self.user_id,
|
45 |
+
"timestamp": self.timestamp
|
46 |
+
}
|
47 |
+
|
48 |
+
|
49 |
+
# CRUD Operations for Logs
|
50 |
+
|
51 |
+
def create_log(level, message, metadata=None, ip_address=None, user_id=None):
|
52 |
+
"""
|
53 |
+
Creates a new log record in the MongoDB collection.
|
54 |
+
|
55 |
+
:param level: Logging level (e.g., "INFO", "ERROR", "WARNING", "DEBUG").
|
56 |
+
:param message: Log message.
|
57 |
+
:param metadata: Optional metadata (e.g., stack trace, request details).
|
58 |
+
:param ip_address: Optional IP address from which the log originated.
|
59 |
+
:param user_id: Optional user ID associated with the log event.
|
60 |
+
:return: The inserted log record's ID (MongoDB ObjectId).
|
61 |
+
"""
|
62 |
+
log_record = LogRecord(
|
63 |
+
level=level,
|
64 |
+
message=message,
|
65 |
+
metadata=metadata,
|
66 |
+
ip_address=ip_address,
|
67 |
+
user_id=user_id
|
68 |
+
)
|
69 |
+
log_id = logs_collection.insert_one(log_record.to_dict()).inserted_id
|
70 |
+
return str(log_id)
|
71 |
+
|
72 |
+
|
73 |
+
def get_log_by_id(log_id):
|
74 |
+
"""
|
75 |
+
Retrieves a log record by its MongoDB ObjectId.
|
76 |
+
|
77 |
+
:param log_id: The MongoDB ObjectId of the log.
|
78 |
+
:return: The log document if found, None if not.
|
79 |
+
"""
|
80 |
+
return logs_collection.find_one({"_id": ObjectId(log_id)})
|
81 |
+
|
82 |
+
|
83 |
+
def get_logs_by_user(user_id):
|
84 |
+
"""
|
85 |
+
Retrieves all log records associated with a specific user.
|
86 |
+
|
87 |
+
:param user_id: The ID of the user associated with the logs.
|
88 |
+
:return: A list of log documents.
|
89 |
+
"""
|
90 |
+
return list(logs_collection.find({"user_id": ObjectId(user_id)}))
|
91 |
+
|
92 |
+
|
93 |
+
def get_logs_by_level(level):
|
94 |
+
"""
|
95 |
+
Retrieves all log records with a specific logging level (e.g., "ERROR").
|
96 |
+
|
97 |
+
:param level: The logging level (e.g., "INFO", "ERROR", "DEBUG").
|
98 |
+
:return: A list of log documents.
|
99 |
+
"""
|
100 |
+
return list(logs_collection.find({"level": level}))
|
101 |
+
|
102 |
+
|
103 |
+
def delete_old_logs():
|
104 |
+
"""
|
105 |
+
Deletes logs that are older than the TTL (30 days). This is automatically handled by the TTL index,
|
106 |
+
so this function can be used to manually trigger a cleanup if necessary.
|
107 |
+
"""
|
108 |
+
# No manual deletion needed as MongoDB TTL will automatically handle the deletion.
|
109 |
+
pass
|
110 |
+
|
111 |
+
|
112 |
+
# Example function to create a sample log
|
113 |
+
if __name__ == "__main__":
|
114 |
+
# Insert a test log record
|
115 |
+
test_level = "ERROR"
|
116 |
+
test_message = "An error occurred in the system."
|
117 |
+
test_metadata = {"stack_trace": "Traceback (most recent call last): ..."}
|
118 |
+
test_ip_address = "192.168.1.1"
|
119 |
+
test_user_id = "610c5e8a85f0b2a4e0b8b918" # Replace with a valid user ObjectId
|
120 |
+
|
121 |
+
log_id = create_log(
|
122 |
+
level=test_level,
|
123 |
+
message=test_message,
|
124 |
+
metadata=test_metadata,
|
125 |
+
ip_address=test_ip_address,
|
126 |
+
user_id=test_user_id
|
127 |
+
)
|
128 |
+
|
129 |
+
print(f"Log record created with ID: {log_id}")
|
models/schemas/usage_schema.py
CHANGED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
import datetime
|
4 |
+
|
5 |
+
# MongoDB connection (move to config/db.py and import here)
|
6 |
+
from config.db import db
|
7 |
+
|
8 |
+
# Define the usage collection
|
9 |
+
usage_collection = db.usage # Collection to store assistant usage records
|
10 |
+
|
11 |
+
|
12 |
+
# Usage Record Schema
|
13 |
+
class UsageRecord:
|
14 |
+
def __init__(self, user_id, manager_id, interaction_type, metadata=None):
|
15 |
+
"""
|
16 |
+
Initializes a UsageRecord object.
|
17 |
+
|
18 |
+
:param user_id: The ID of the user who interacted with the assistant.
|
19 |
+
:param manager_id: The ID of the user's manager.
|
20 |
+
:param interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
21 |
+
:param metadata: Optional. Additional metadata for the interaction (e.g., details of the query or response).
|
22 |
+
"""
|
23 |
+
self.user_id = ObjectId(user_id) # The user interacting with the assistant
|
24 |
+
self.manager_id = ObjectId(manager_id) # The manager overseeing the user
|
25 |
+
self.interaction_type = interaction_type # Type of interaction (e.g., disease detection, inventory management)
|
26 |
+
self.metadata = metadata if metadata else {}
|
27 |
+
self.timestamp = datetime.datetime.utcnow() # Timestamp of the interaction
|
28 |
+
|
29 |
+
def to_dict(self):
|
30 |
+
"""
|
31 |
+
Converts the UsageRecord object to a dictionary format for MongoDB insertion.
|
32 |
+
:return: dict
|
33 |
+
"""
|
34 |
+
return {
|
35 |
+
"user_id": self.user_id,
|
36 |
+
"manager_id": self.manager_id,
|
37 |
+
"interaction_type": self.interaction_type,
|
38 |
+
"metadata": self.metadata,
|
39 |
+
"timestamp": self.timestamp
|
40 |
+
}
|
41 |
+
|
42 |
+
|
43 |
+
# CRUD Operations for Usage Records
|
44 |
+
|
45 |
+
def create_usage_record(user_id, manager_id, interaction_type, metadata=None):
|
46 |
+
"""
|
47 |
+
Creates a new usage record in the MongoDB collection.
|
48 |
+
|
49 |
+
:param user_id: The ID of the user interacting with the assistant.
|
50 |
+
:param manager_id: The ID of the manager overseeing the user.
|
51 |
+
:param interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
52 |
+
:param metadata: Optional. Additional metadata for the interaction.
|
53 |
+
:return: The inserted usage record's ID (MongoDB ObjectId).
|
54 |
+
"""
|
55 |
+
usage_record = UsageRecord(
|
56 |
+
user_id=user_id,
|
57 |
+
manager_id=manager_id,
|
58 |
+
interaction_type=interaction_type,
|
59 |
+
metadata=metadata
|
60 |
+
)
|
61 |
+
record_id = usage_collection.insert_one(usage_record.to_dict()).inserted_id
|
62 |
+
return str(record_id)
|
63 |
+
|
64 |
+
|
65 |
+
def get_usage_by_user(user_id):
|
66 |
+
"""
|
67 |
+
Retrieves all usage records for a specific user.
|
68 |
+
|
69 |
+
:param user_id: The ID of the user.
|
70 |
+
:return: A list of usage record documents created by the user.
|
71 |
+
"""
|
72 |
+
return list(usage_collection.find({"user_id": ObjectId(user_id)}))
|
73 |
+
|
74 |
+
|
75 |
+
def get_usage_by_manager(manager_id):
|
76 |
+
"""
|
77 |
+
Retrieves all usage records for users under a specific manager.
|
78 |
+
|
79 |
+
:param manager_id: The ID of the manager.
|
80 |
+
:return: A list of usage record documents for users managed by the manager.
|
81 |
+
"""
|
82 |
+
return list(usage_collection.find({"manager_id": ObjectId(manager_id)}))
|
83 |
+
|
84 |
+
|
85 |
+
def get_usage_by_interaction_type(interaction_type):
|
86 |
+
"""
|
87 |
+
Retrieves all usage records with a specific interaction type (e.g., "disease_detection").
|
88 |
+
|
89 |
+
:param interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
90 |
+
:return: A list of usage record documents for the specified interaction type.
|
91 |
+
"""
|
92 |
+
return list(usage_collection.find({"interaction_type": interaction_type}))
|
93 |
+
|
94 |
+
|
95 |
+
def delete_usage_record(record_id):
|
96 |
+
"""
|
97 |
+
Deletes a usage record from the MongoDB collection by its ObjectId.
|
98 |
+
|
99 |
+
:param record_id: The MongoDB ObjectId of the usage record.
|
100 |
+
:return: Boolean, True if the deletion was successful, False otherwise.
|
101 |
+
"""
|
102 |
+
result = usage_collection.delete_one({"_id": ObjectId(record_id)})
|
103 |
+
return result.deleted_count > 0
|
104 |
+
|
105 |
+
|
106 |
+
# Example function to create a sample usage record
|
107 |
+
if __name__ == "__main__":
|
108 |
+
# Insert a test usage record
|
109 |
+
test_user_id = "610c5e8a85f0b2a4e0b8b918" # Replace with a valid user ObjectId
|
110 |
+
test_manager_id = "610c5e8a85f0b2a4e0b8b919" # Replace with a valid manager ObjectId
|
111 |
+
test_interaction_type = "disease_detection"
|
112 |
+
test_metadata = {"disease_name": "Coccidiosis", "detected": True}
|
113 |
+
|
114 |
+
record_id = create_usage_record(
|
115 |
+
user_id=test_user_id,
|
116 |
+
manager_id=test_manager_id,
|
117 |
+
interaction_type=test_interaction_type,
|
118 |
+
metadata=test_metadata
|
119 |
+
)
|
120 |
+
|
121 |
+
print(f"Usage record created with ID: {record_id}")
|
requirements.txt
CHANGED
@@ -1,11 +1,28 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
numpy~=1.23.5
|
7 |
torchvision
|
|
|
|
|
|
|
|
|
8 |
accelerate
|
9 |
-
|
|
|
|
|
|
|
|
|
|
|
10 |
pymongo
|
11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Machine Learning Libraries
|
2 |
+
tensorflow
|
3 |
+
tensorflow-gpu
|
4 |
+
keras
|
5 |
+
torch
|
|
|
6 |
torchvision
|
7 |
+
numpy
|
8 |
+
|
9 |
+
# Transformers and AI model handling
|
10 |
+
transformers
|
11 |
accelerate
|
12 |
+
|
13 |
+
# Image Processing Libraries
|
14 |
+
opencv-python
|
15 |
+
|
16 |
+
# Web Framework and MongoDB
|
17 |
+
flask
|
18 |
pymongo
|
19 |
+
bson
|
20 |
+
|
21 |
+
# Gradio Interface for the Chatbot
|
22 |
+
gradio
|
23 |
+
|
24 |
+
# Data Handling and Analysis
|
25 |
+
pandas
|
26 |
+
|
27 |
+
# OpenAI Integration
|
28 |
+
openai
|
routes/api.py
CHANGED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Blueprint, jsonify, request
|
2 |
+
from controllers.health_management import detect_and_log_disease, get_health_record, get_health_records_for_user
|
3 |
+
from controllers.inventory import add_inventory_item, update_inventory_item_by_id, get_inventory_by_id, \
|
4 |
+
get_all_inventory
|
5 |
+
from controllers.usage_tracking import log_usage, get_usage_for_user, get_usage_for_manager, get_usage_by_type
|
6 |
+
from controllers.data_logging import log_user_action
|
7 |
+
|
8 |
+
# Create a Blueprint for the API routes
|
9 |
+
api_bp = Blueprint('api', __name__)
|
10 |
+
|
11 |
+
|
12 |
+
# Health Management Routes
|
13 |
+
@api_bp.route('/detect-disease', methods=['POST'])
|
14 |
+
def detect_disease_route():
|
15 |
+
"""
|
16 |
+
Route for detecting a disease and logging the health record.
|
17 |
+
Expects a POST request with:
|
18 |
+
- user_id: ID of the user.
|
19 |
+
- poultry_id: ID of the poultry.
|
20 |
+
- image: The image of poultry feces for disease detection.
|
21 |
+
|
22 |
+
Returns: JSON response with disease detection results and log status.
|
23 |
+
"""
|
24 |
+
return detect_and_log_disease()
|
25 |
+
|
26 |
+
|
27 |
+
@api_bp.route('/health-record/<record_id>', methods=['GET'])
|
28 |
+
def get_health_record_route(record_id):
|
29 |
+
"""
|
30 |
+
Route for retrieving a health record by its ID.
|
31 |
+
|
32 |
+
Returns: JSON response with the health record details.
|
33 |
+
"""
|
34 |
+
return get_health_record(record_id)
|
35 |
+
|
36 |
+
|
37 |
+
@api_bp.route('/health-records/<user_id>', methods=['GET'])
|
38 |
+
def get_health_records_for_user_route(user_id):
|
39 |
+
"""
|
40 |
+
Route for retrieving all health records for a specific user.
|
41 |
+
|
42 |
+
Returns: JSON response with the list of health records.
|
43 |
+
"""
|
44 |
+
return get_health_records_for_user(user_id)
|
45 |
+
|
46 |
+
|
47 |
+
# Inventory Management Routes
|
48 |
+
@api_bp.route('/add-inventory-item', methods=['POST'])
|
49 |
+
def add_inventory_item_route():
|
50 |
+
"""
|
51 |
+
Route for adding a new inventory item.
|
52 |
+
Expects a POST request with:
|
53 |
+
- user_id: ID of the user.
|
54 |
+
- item_name: Name of the item.
|
55 |
+
- category: Category of the item.
|
56 |
+
- quantity: Quantity of the item.
|
57 |
+
- unit: Unit of measurement.
|
58 |
+
|
59 |
+
Returns: JSON response with the item addition status.
|
60 |
+
"""
|
61 |
+
return add_inventory_item()
|
62 |
+
|
63 |
+
|
64 |
+
@api_bp.route('/update-inventory-item', methods=['PUT'])
|
65 |
+
def update_inventory_item_route():
|
66 |
+
"""
|
67 |
+
Route for updating an inventory item.
|
68 |
+
Expects a PUT request with:
|
69 |
+
- user_id: ID of the user.
|
70 |
+
- item_id: ID of the inventory item to update.
|
71 |
+
- updated_fields: Fields to update (e.g., quantity, used_quantity).
|
72 |
+
|
73 |
+
Returns: JSON response with the item update status.
|
74 |
+
"""
|
75 |
+
return update_inventory_item_by_id()
|
76 |
+
|
77 |
+
|
78 |
+
@api_bp.route('/inventory-item/<item_id>', methods=['GET'])
|
79 |
+
def get_inventory_item_route(item_id):
|
80 |
+
"""
|
81 |
+
Route for retrieving an inventory item by its ID.
|
82 |
+
|
83 |
+
Returns: JSON response with the inventory item details.
|
84 |
+
"""
|
85 |
+
return get_inventory_by_id(item_id)
|
86 |
+
|
87 |
+
|
88 |
+
@api_bp.route('/inventory', methods=['GET'])
|
89 |
+
def get_all_inventory_route():
|
90 |
+
"""
|
91 |
+
Route for retrieving all inventory items.
|
92 |
+
|
93 |
+
Returns: JSON response with the list of all inventory items.
|
94 |
+
"""
|
95 |
+
return get_all_inventory()
|
96 |
+
|
97 |
+
|
98 |
+
# Assistant Usage Tracking Routes
|
99 |
+
@api_bp.route('/log-usage', methods=['POST'])
|
100 |
+
def log_usage_route():
|
101 |
+
"""
|
102 |
+
Route for logging the usage of the assistant by a user.
|
103 |
+
Expects a POST request with:
|
104 |
+
- user_id: ID of the user.
|
105 |
+
- manager_id: ID of the manager.
|
106 |
+
- interaction_type: The type of interaction.
|
107 |
+
|
108 |
+
Returns: JSON response with the log status.
|
109 |
+
"""
|
110 |
+
return log_usage()
|
111 |
+
|
112 |
+
|
113 |
+
@api_bp.route('/usage/<user_id>', methods=['GET'])
|
114 |
+
def get_usage_for_user_route(user_id):
|
115 |
+
"""
|
116 |
+
Route for retrieving all usage logs for a specific user.
|
117 |
+
|
118 |
+
Returns: JSON response with the list of usage logs for the user.
|
119 |
+
"""
|
120 |
+
return get_usage_for_user(user_id)
|
121 |
+
|
122 |
+
|
123 |
+
@api_bp.route('/manager-usage/<manager_id>', methods=['GET'])
|
124 |
+
def get_usage_for_manager_route(manager_id):
|
125 |
+
"""
|
126 |
+
Route for retrieving all usage logs for users managed by a specific manager.
|
127 |
+
|
128 |
+
Returns: JSON response with the list of usage logs for the manager's users.
|
129 |
+
"""
|
130 |
+
return get_usage_for_manager(manager_id)
|
131 |
+
|
132 |
+
|
133 |
+
@api_bp.route('/usage-type/<interaction_type>', methods=['GET'])
|
134 |
+
def get_usage_by_type_route(interaction_type):
|
135 |
+
"""
|
136 |
+
Route for retrieving all usage logs for a specific interaction type.
|
137 |
+
|
138 |
+
Returns: JSON response with the list of usage logs for the interaction type.
|
139 |
+
"""
|
140 |
+
return get_usage_by_type(interaction_type)
|
141 |
+
|
142 |
+
|
143 |
+
# User Action Logging Route
|
144 |
+
@api_bp.route('/log-user-action', methods=['POST'])
|
145 |
+
def log_user_action_route():
|
146 |
+
"""
|
147 |
+
Route for logging general user actions.
|
148 |
+
Expects a POST request with:
|
149 |
+
- user_id: ID of the user performing the action.
|
150 |
+
- message: Description of the action.
|
151 |
+
- level: Logging level (default is "INFO").
|
152 |
+
|
153 |
+
Returns: JSON response with the log status.
|
154 |
+
"""
|
155 |
+
user_id = request.json.get('user_id')
|
156 |
+
message = request.json.get('message')
|
157 |
+
level = request.json.get('level', 'INFO')
|
158 |
+
metadata = request.json.get('metadata', {})
|
159 |
+
|
160 |
+
return log_user_action(user_id, message, level, metadata)
|
routes/data_logging_routes.py
CHANGED
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Blueprint, request, jsonify
|
2 |
+
from controllers.data_logging import log_user_action, log_health_record, log_inventory_addition, log_inventory_update, \
|
3 |
+
log_assistant_usage
|
4 |
+
|
5 |
+
# Create a Blueprint for the data logging routes
|
6 |
+
data_logging_bp = Blueprint('data_logging', __name__)
|
7 |
+
|
8 |
+
|
9 |
+
# Route to log user actions
|
10 |
+
@data_logging_bp.route('/log-user-action', methods=['POST'])
|
11 |
+
def log_user_action_route():
|
12 |
+
"""
|
13 |
+
Logs a general user action.
|
14 |
+
|
15 |
+
Expects a POST request with:
|
16 |
+
- user_id: ID of the user performing the action.
|
17 |
+
- message: Description of the action.
|
18 |
+
- level: Logging level (default is "INFO").
|
19 |
+
- metadata: Optional. Additional metadata related to the action.
|
20 |
+
|
21 |
+
Returns: JSON response indicating success or failure.
|
22 |
+
"""
|
23 |
+
try:
|
24 |
+
user_id = request.json.get('user_id')
|
25 |
+
message = request.json.get('message')
|
26 |
+
level = request.json.get('level', 'INFO')
|
27 |
+
metadata = request.json.get('metadata', {})
|
28 |
+
|
29 |
+
if not user_id or not message:
|
30 |
+
return jsonify({"error": "Missing required fields"}), 400
|
31 |
+
|
32 |
+
return log_user_action(user_id, message, level, metadata)
|
33 |
+
|
34 |
+
except Exception as e:
|
35 |
+
return jsonify({"error": str(e)}), 500
|
36 |
+
|
37 |
+
|
38 |
+
# Route to log a health record
|
39 |
+
@data_logging_bp.route('/log-health-record', methods=['POST'])
|
40 |
+
def log_health_record_route():
|
41 |
+
"""
|
42 |
+
Logs a health record for poultry.
|
43 |
+
|
44 |
+
Expects a POST request with:
|
45 |
+
- user_id: ID of the user submitting the record.
|
46 |
+
- poultry_id: The ID of the poultry.
|
47 |
+
- disease_name: Name of the detected disease.
|
48 |
+
- status: The health status of the poultry.
|
49 |
+
- recommendation: Recommended treatment.
|
50 |
+
- image_path: Optional. Path to an image related to the health record.
|
51 |
+
|
52 |
+
Returns: JSON response indicating success or failure.
|
53 |
+
"""
|
54 |
+
try:
|
55 |
+
user_id = request.json.get('user_id')
|
56 |
+
poultry_id = request.json.get('poultry_id')
|
57 |
+
disease_name = request.json.get('disease_name')
|
58 |
+
status = request.json.get('status')
|
59 |
+
recommendation = request.json.get('recommendation')
|
60 |
+
image_path = request.json.get('image_path')
|
61 |
+
|
62 |
+
if not user_id or not poultry_id or not disease_name or not status or not recommendation:
|
63 |
+
return jsonify({"error": "Missing required fields"}), 400
|
64 |
+
|
65 |
+
return log_health_record(user_id, poultry_id, disease_name, status, recommendation, image_path)
|
66 |
+
|
67 |
+
except Exception as e:
|
68 |
+
return jsonify({"error": str(e)}), 500
|
69 |
+
|
70 |
+
|
71 |
+
# Route to log inventory addition
|
72 |
+
@data_logging_bp.route('/log-inventory-addition', methods=['POST'])
|
73 |
+
def log_inventory_addition_route():
|
74 |
+
"""
|
75 |
+
Logs the addition of an inventory item.
|
76 |
+
|
77 |
+
Expects a POST request with:
|
78 |
+
- user_id: ID of the user adding the item.
|
79 |
+
- item_name: Name of the inventory item.
|
80 |
+
- category: Category of the item (e.g., "Feed", "Medicine").
|
81 |
+
- quantity: Total quantity added.
|
82 |
+
- unit: Unit of measurement (e.g., "kg", "liters").
|
83 |
+
- description: Optional. Additional description or notes.
|
84 |
+
|
85 |
+
Returns: JSON response indicating success or failure.
|
86 |
+
"""
|
87 |
+
try:
|
88 |
+
user_id = request.json.get('user_id')
|
89 |
+
item_name = request.json.get('item_name')
|
90 |
+
category = request.json.get('category')
|
91 |
+
quantity = request.json.get('quantity')
|
92 |
+
unit = request.json.get('unit')
|
93 |
+
description = request.json.get('description')
|
94 |
+
|
95 |
+
if not user_id or not item_name or not category or not quantity or not unit:
|
96 |
+
return jsonify({"error": "Missing required fields"}), 400
|
97 |
+
|
98 |
+
return log_inventory_addition(user_id, item_name, category, quantity, unit, description)
|
99 |
+
|
100 |
+
except Exception as e:
|
101 |
+
return jsonify({"error": str(e)}), 500
|
102 |
+
|
103 |
+
|
104 |
+
# Route to log inventory update
|
105 |
+
@data_logging_bp.route('/log-inventory-update', methods=['PUT'])
|
106 |
+
def log_inventory_update_route():
|
107 |
+
"""
|
108 |
+
Logs an update to an existing inventory item.
|
109 |
+
|
110 |
+
Expects a PUT request with:
|
111 |
+
- user_id: ID of the user updating the item.
|
112 |
+
- item_id: ID of the inventory item being updated.
|
113 |
+
- updated_fields: Fields being updated (e.g., "quantity", "used_quantity").
|
114 |
+
|
115 |
+
Returns: JSON response indicating success or failure.
|
116 |
+
"""
|
117 |
+
try:
|
118 |
+
user_id = request.json.get('user_id')
|
119 |
+
item_id = request.json.get('item_id')
|
120 |
+
updated_fields = request.json.get('updated_fields')
|
121 |
+
|
122 |
+
if not user_id or not item_id or not updated_fields:
|
123 |
+
return jsonify({"error": "Missing required fields"}), 400
|
124 |
+
|
125 |
+
return log_inventory_update(user_id, item_id, updated_fields)
|
126 |
+
|
127 |
+
except Exception as e:
|
128 |
+
return jsonify({"error": str(e)}), 500
|
129 |
+
|
130 |
+
|
131 |
+
# Route to log assistant usage
|
132 |
+
@data_logging_bp.route('/log-assistant-usage', methods=['POST'])
|
133 |
+
def log_assistant_usage_route():
|
134 |
+
"""
|
135 |
+
Logs the usage of the assistant by a user.
|
136 |
+
|
137 |
+
Expects a POST request with:
|
138 |
+
- user_id: ID of the user.
|
139 |
+
- manager_id: ID of the manager overseeing the user.
|
140 |
+
- interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
141 |
+
- metadata: Optional. Additional details about the interaction.
|
142 |
+
|
143 |
+
Returns: JSON response indicating success or failure.
|
144 |
+
"""
|
145 |
+
try:
|
146 |
+
user_id = request.json.get('user_id')
|
147 |
+
manager_id = request.json.get('manager_id')
|
148 |
+
interaction_type = request.json.get('interaction_type')
|
149 |
+
metadata = request.json.get('metadata')
|
150 |
+
|
151 |
+
if not user_id or not manager_id or not interaction_type:
|
152 |
+
return jsonify({"error": "Missing required fields"}), 400
|
153 |
+
|
154 |
+
return log_assistant_usage(user_id, manager_id, interaction_type, metadata)
|
155 |
+
|
156 |
+
except Exception as e:
|
157 |
+
return jsonify({"error": str(e)}), 500
|
routes/health_routes.py
CHANGED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Blueprint, request, jsonify
|
2 |
+
from controllers.health_management import detect_and_log_disease, get_health_record, get_health_records_for_user
|
3 |
+
|
4 |
+
# Create a Blueprint for the health-related routes
|
5 |
+
health_bp = Blueprint('health', __name__)
|
6 |
+
|
7 |
+
|
8 |
+
# Route to detect disease and log the health record
|
9 |
+
@health_bp.route('/detect-disease', methods=['POST'])
|
10 |
+
def detect_disease_route():
|
11 |
+
"""
|
12 |
+
Detect a poultry disease and log the health record.
|
13 |
+
|
14 |
+
Expects a POST request with the following fields:
|
15 |
+
- user_id: ID of the user.
|
16 |
+
- poultry_id: ID of the poultry.
|
17 |
+
- image: Image of poultry feces for disease detection.
|
18 |
+
|
19 |
+
Returns: JSON response with disease detection result and health record log status.
|
20 |
+
"""
|
21 |
+
try:
|
22 |
+
return detect_and_log_disease()
|
23 |
+
except Exception as e:
|
24 |
+
return jsonify({"error": str(e)}), 500
|
25 |
+
|
26 |
+
|
27 |
+
# Route to get a health record by its ID
|
28 |
+
@health_bp.route('/health-record/<record_id>', methods=['GET'])
|
29 |
+
def get_health_record_route(record_id):
|
30 |
+
"""
|
31 |
+
Retrieve a health record by its ID.
|
32 |
+
|
33 |
+
Expects a GET request with the health record ID in the URL.
|
34 |
+
|
35 |
+
Returns: JSON response with the health record details.
|
36 |
+
"""
|
37 |
+
try:
|
38 |
+
return get_health_record(record_id)
|
39 |
+
except Exception as e:
|
40 |
+
return jsonify({"error": str(e)}), 500
|
41 |
+
|
42 |
+
|
43 |
+
# Route to get all health records for a specific user
|
44 |
+
@health_bp.route('/health-records/<user_id>', methods=['GET'])
|
45 |
+
def get_health_records_for_user_route(user_id):
|
46 |
+
"""
|
47 |
+
Retrieve all health records for a specific user.
|
48 |
+
|
49 |
+
Expects a GET request with the user ID in the URL.
|
50 |
+
|
51 |
+
Returns: JSON response with the list of health records for the user.
|
52 |
+
"""
|
53 |
+
try:
|
54 |
+
return get_health_records_for_user(user_id)
|
55 |
+
except Exception as e:
|
56 |
+
return jsonify({"error": str(e)}), 500
|
routes/inventory_routes.py
CHANGED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Blueprint, request, jsonify
|
2 |
+
from controllers.inventory import add_inventory_item, update_inventory_item_by_id, get_inventory_by_id, \
|
3 |
+
get_all_inventory
|
4 |
+
|
5 |
+
# Create a Blueprint for the inventory-related routes
|
6 |
+
inventory_bp = Blueprint('inventory', __name__)
|
7 |
+
|
8 |
+
|
9 |
+
# Route to add a new inventory item
|
10 |
+
@inventory_bp.route('/add-inventory-item', methods=['POST'])
|
11 |
+
def add_inventory_item_route():
|
12 |
+
"""
|
13 |
+
Add a new inventory item to the system.
|
14 |
+
|
15 |
+
Expects a POST request with the following fields:
|
16 |
+
- user_id: The ID of the user adding the item.
|
17 |
+
- item_name: The name of the inventory item.
|
18 |
+
- category: The category of the item (e.g., "Feed", "Medicine").
|
19 |
+
- quantity: The total quantity of the item.
|
20 |
+
- unit: The unit of measurement (e.g., "kg", "liters").
|
21 |
+
- description: Optional. Additional description or notes about the item.
|
22 |
+
|
23 |
+
Returns: JSON response with the status of the addition.
|
24 |
+
"""
|
25 |
+
try:
|
26 |
+
return add_inventory_item()
|
27 |
+
except Exception as e:
|
28 |
+
return jsonify({"error": str(e)}), 500
|
29 |
+
|
30 |
+
|
31 |
+
# Route to update an existing inventory item
|
32 |
+
@inventory_bp.route('/update-inventory-item', methods=['PUT'])
|
33 |
+
def update_inventory_item_route():
|
34 |
+
"""
|
35 |
+
Update an existing inventory item in the system.
|
36 |
+
|
37 |
+
Expects a PUT request with the following fields:
|
38 |
+
- user_id: The ID of the user updating the item.
|
39 |
+
- item_id: The ID of the inventory item to be updated.
|
40 |
+
- updated_fields: Dictionary containing fields to be updated (e.g., quantity, used_quantity).
|
41 |
+
|
42 |
+
Returns: JSON response with the status of the update.
|
43 |
+
"""
|
44 |
+
try:
|
45 |
+
return update_inventory_item_by_id()
|
46 |
+
except Exception as e:
|
47 |
+
return jsonify({"error": str(e)}), 500
|
48 |
+
|
49 |
+
|
50 |
+
# Route to get an inventory item by its ID
|
51 |
+
@inventory_bp.route('/inventory-item/<item_id>', methods=['GET'])
|
52 |
+
def get_inventory_item_route(item_id):
|
53 |
+
"""
|
54 |
+
Retrieve an inventory item by its ID.
|
55 |
+
|
56 |
+
Expects a GET request with the item_id as a URL parameter.
|
57 |
+
|
58 |
+
Returns: JSON response with the inventory item details.
|
59 |
+
"""
|
60 |
+
try:
|
61 |
+
return get_inventory_by_id(item_id)
|
62 |
+
except Exception as e:
|
63 |
+
return jsonify({"error": str(e)}), 500
|
64 |
+
|
65 |
+
|
66 |
+
# Route to get all inventory items
|
67 |
+
@inventory_bp.route('/inventory', methods=['GET'])
|
68 |
+
def get_all_inventory_route():
|
69 |
+
"""
|
70 |
+
Retrieve all inventory items in the system.
|
71 |
+
|
72 |
+
Returns: JSON response with the list of all inventory items.
|
73 |
+
"""
|
74 |
+
try:
|
75 |
+
return get_all_inventory()
|
76 |
+
except Exception as e:
|
77 |
+
return jsonify({"error": str(e)}), 500
|
routes/usage_routes.py
CHANGED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Blueprint, request, jsonify
|
2 |
+
from controllers.usage_tracking import log_usage, get_usage_for_user, get_usage_for_manager, get_usage_by_type
|
3 |
+
|
4 |
+
# Create a Blueprint for the usage-related routes
|
5 |
+
usage_bp = Blueprint('usage', __name__)
|
6 |
+
|
7 |
+
|
8 |
+
# Route to log the usage of the assistant by a user
|
9 |
+
@usage_bp.route('/log-usage', methods=['POST'])
|
10 |
+
def log_usage_route():
|
11 |
+
"""
|
12 |
+
Logs the usage of the assistant by a user.
|
13 |
+
|
14 |
+
Expects a POST request with the following fields:
|
15 |
+
- user_id: The ID of the user interacting with the assistant.
|
16 |
+
- manager_id: The ID of the user's manager.
|
17 |
+
- interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
18 |
+
- metadata: Optional. Additional details about the interaction (e.g., query details or results).
|
19 |
+
|
20 |
+
Returns: JSON response indicating success or failure.
|
21 |
+
"""
|
22 |
+
try:
|
23 |
+
return log_usage()
|
24 |
+
except Exception as e:
|
25 |
+
return jsonify({"error": str(e)}), 500
|
26 |
+
|
27 |
+
|
28 |
+
# Route to get all assistant usage logs for a specific user
|
29 |
+
@usage_bp.route('/usage/<user_id>', methods=['GET'])
|
30 |
+
def get_usage_for_user_route(user_id):
|
31 |
+
"""
|
32 |
+
Retrieve all assistant usage logs for a specific user.
|
33 |
+
|
34 |
+
Expects a GET request with the user_id as a URL parameter.
|
35 |
+
|
36 |
+
Returns: JSON response with the list of usage logs for the user.
|
37 |
+
"""
|
38 |
+
try:
|
39 |
+
return get_usage_for_user(user_id)
|
40 |
+
except Exception as e:
|
41 |
+
return jsonify({"error": str(e)}), 500
|
42 |
+
|
43 |
+
|
44 |
+
# Route to get all assistant usage logs for users managed by a specific manager
|
45 |
+
@usage_bp.route('/manager-usage/<manager_id>', methods=['GET'])
|
46 |
+
def get_usage_for_manager_route(manager_id):
|
47 |
+
"""
|
48 |
+
Retrieve all assistant usage logs for users managed by a specific manager.
|
49 |
+
|
50 |
+
Expects a GET request with the manager_id as a URL parameter.
|
51 |
+
|
52 |
+
Returns: JSON response with the list of usage logs for the manager's users.
|
53 |
+
"""
|
54 |
+
try:
|
55 |
+
return get_usage_for_manager(manager_id)
|
56 |
+
except Exception as e:
|
57 |
+
return jsonify({"error": str(e)}), 500
|
58 |
+
|
59 |
+
|
60 |
+
# Route to get all assistant usage logs for a specific interaction type
|
61 |
+
@usage_bp.route('/usage-type/<interaction_type>', methods=['GET'])
|
62 |
+
def get_usage_by_type_route(interaction_type):
|
63 |
+
"""
|
64 |
+
Retrieve all assistant usage logs for a specific interaction type.
|
65 |
+
|
66 |
+
Expects a GET request with the interaction_type as a URL parameter (e.g., "disease_detection", "inventory_management").
|
67 |
+
|
68 |
+
Returns: JSON response with the list of usage logs for the specified interaction type.
|
69 |
+
"""
|
70 |
+
try:
|
71 |
+
return get_usage_by_type(interaction_type)
|
72 |
+
except Exception as e:
|
73 |
+
return jsonify({"error": str(e)}), 500
|
services/disease_detection.py
CHANGED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
from keras.models import load_model
|
4 |
+
|
5 |
+
# Load the model (ensure the path is correct)
|
6 |
+
my_model = load_model('models/Final_Chicken_disease_model.h5')
|
7 |
+
|
8 |
+
# Disease names and recommendations
|
9 |
+
name_disease = {0: 'Coccidiosis', 1: 'Healthy', 2: 'New Castle Disease', 3: 'Salmonella'}
|
10 |
+
result = {0: 'Critical', 1: 'No issue', 2: 'Critical', 3: 'Critical'}
|
11 |
+
recommend = {0: 'Panadol', 1: 'You have no need Medicine', 2: 'Percetamol', 3: 'Ponston'}
|
12 |
+
|
13 |
+
|
14 |
+
class PoultryFarmBot:
|
15 |
+
def __init__(self, db):
|
16 |
+
self.db = db # MongoDB database for future use
|
17 |
+
|
18 |
+
def preprocess_image(self, image):
|
19 |
+
try:
|
20 |
+
image_check = cv2.resize(image, (224, 224))
|
21 |
+
image_check = np.expand_dims(image_check, axis=0)
|
22 |
+
return image_check
|
23 |
+
except Exception as e:
|
24 |
+
print(f"Error in image preprocessing: {e}")
|
25 |
+
return None
|
26 |
+
|
27 |
+
def predict(self, image):
|
28 |
+
image_check = self.preprocess_image(image)
|
29 |
+
if image_check is None:
|
30 |
+
return "Image preprocessing failed.", None, None, None
|
31 |
+
indx = my_model.predict(image_check).argmax()
|
32 |
+
name = name_disease.get(indx, "Unknown disease")
|
33 |
+
status = result.get(indx, "unknown condition")
|
34 |
+
recom = recommend.get(indx, "no recommendation available")
|
35 |
+
return name, status, recom
|
services/gradio_interface.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from services.disease_detection import PoultryFarmBot
|
3 |
+
from services.llama_service import llama2_response
|
4 |
+
|
5 |
+
|
6 |
+
def chatbot_response(image, text, bot):
|
7 |
+
if image is not None:
|
8 |
+
diagnosis, name, status, recom = bot.predict(image)
|
9 |
+
return f"Disease: {name}, Status: {status}, Recommendation: {recom}" if name else "No disease detected."
|
10 |
+
else:
|
11 |
+
return llama2_response(text)
|
12 |
+
|
13 |
+
|
14 |
+
def create_gradio_interface():
|
15 |
+
bot = PoultryFarmBot(db=None) # Pass your MongoDB instance here if needed
|
16 |
+
|
17 |
+
with gr.Blocks(theme=gr.themes.Soft(primary_hue="green", neutral_hue="slate")) as interface:
|
18 |
+
gr.Markdown("# 🐔 Poultry Management Chatbot")
|
19 |
+
gr.Markdown(
|
20 |
+
"This chatbot can help you manage your poultry with conversational AI. Upload an image or ask a question!")
|
21 |
+
|
22 |
+
with gr.Row():
|
23 |
+
with gr.Column(scale=1):
|
24 |
+
fecal_image = gr.Image(label="Upload Image of Poultry Feces (Optional)", type="numpy", show_label=True)
|
25 |
+
with gr.Column(scale=2):
|
26 |
+
user_input = gr.Textbox(label="Type your question", placeholder="Ask about poultry management...",
|
27 |
+
lines=3)
|
28 |
+
|
29 |
+
output_box = gr.Textbox(label="Response", interactive=False, lines=10)
|
30 |
+
submit_button = gr.Button("Submit")
|
31 |
+
submit_button.click(fn=lambda image, text: chatbot_response(image, text, bot),
|
32 |
+
inputs=[fecal_image, user_input], outputs=[output_box])
|
33 |
+
|
34 |
+
return interface
|
services/inventory_manager.py
CHANGED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
import datetime
|
4 |
+
|
5 |
+
# MongoDB Setup (import from your db configuration)
|
6 |
+
from config.db import db
|
7 |
+
|
8 |
+
# Define the inventory collection
|
9 |
+
inventory_collection = db.inventory # MongoDB collection to store inventory items
|
10 |
+
|
11 |
+
|
12 |
+
# Inventory Management Service
|
13 |
+
|
14 |
+
class InventoryManager:
|
15 |
+
"""
|
16 |
+
Inventory Manager Service to handle adding, updating, and retrieving inventory items.
|
17 |
+
"""
|
18 |
+
|
19 |
+
def __init__(self):
|
20 |
+
self.collection = inventory_collection
|
21 |
+
|
22 |
+
def add_inventory_item(self, user_id, item_name, category, quantity, unit, description=None):
|
23 |
+
"""
|
24 |
+
Add a new item to the inventory.
|
25 |
+
|
26 |
+
:param user_id: The ID of the user adding the item.
|
27 |
+
:param item_name: The name of the inventory item.
|
28 |
+
:param category: The category of the item (e.g., "Feed", "Medicine").
|
29 |
+
:param quantity: The total quantity of the item.
|
30 |
+
:param unit: The unit of measurement (e.g., "kg", "liters").
|
31 |
+
:param description: Optional. Additional description or notes about the item.
|
32 |
+
:return: The inserted inventory item's ID.
|
33 |
+
"""
|
34 |
+
try:
|
35 |
+
new_item = {
|
36 |
+
"user_id": ObjectId(user_id),
|
37 |
+
"item_name": item_name,
|
38 |
+
"category": category,
|
39 |
+
"quantity": quantity,
|
40 |
+
"unit": unit,
|
41 |
+
"description": description,
|
42 |
+
"created_at": datetime.datetime.utcnow(),
|
43 |
+
"updated_at": datetime.datetime.utcnow()
|
44 |
+
}
|
45 |
+
result = self.collection.insert_one(new_item)
|
46 |
+
return str(result.inserted_id)
|
47 |
+
except Exception as e:
|
48 |
+
print(f"Error adding inventory item: {e}")
|
49 |
+
return None
|
50 |
+
|
51 |
+
def update_inventory_item(self, item_id, updated_fields):
|
52 |
+
"""
|
53 |
+
Update an existing inventory item with new values.
|
54 |
+
|
55 |
+
:param item_id: The ID of the inventory item to be updated.
|
56 |
+
:param updated_fields: A dictionary containing the fields to be updated (e.g., quantity, description).
|
57 |
+
:return: Boolean indicating whether the update was successful.
|
58 |
+
"""
|
59 |
+
try:
|
60 |
+
updated_fields['updated_at'] = datetime.datetime.utcnow() # Update timestamp
|
61 |
+
result = self.collection.update_one(
|
62 |
+
{"_id": ObjectId(item_id)},
|
63 |
+
{"$set": updated_fields}
|
64 |
+
)
|
65 |
+
return result.modified_count > 0
|
66 |
+
except Exception as e:
|
67 |
+
print(f"Error updating inventory item: {e}")
|
68 |
+
return False
|
69 |
+
|
70 |
+
def get_inventory_item_by_id(self, item_id):
|
71 |
+
"""
|
72 |
+
Retrieve an inventory item by its ID.
|
73 |
+
|
74 |
+
:param item_id: The ID of the inventory item.
|
75 |
+
:return: The inventory item document or None if not found.
|
76 |
+
"""
|
77 |
+
try:
|
78 |
+
return self.collection.find_one({"_id": ObjectId(item_id)})
|
79 |
+
except Exception as e:
|
80 |
+
print(f"Error retrieving inventory item: {e}")
|
81 |
+
return None
|
82 |
+
|
83 |
+
def get_all_inventory_items(self, user_id=None):
|
84 |
+
"""
|
85 |
+
Retrieve all inventory items, optionally filtered by user ID.
|
86 |
+
|
87 |
+
:param user_id: Optional. The ID of the user to filter by.
|
88 |
+
:return: A list of inventory items or an empty list if none are found.
|
89 |
+
"""
|
90 |
+
try:
|
91 |
+
if user_id:
|
92 |
+
return list(self.collection.find({"user_id": ObjectId(user_id)}))
|
93 |
+
else:
|
94 |
+
return list(self.collection.find())
|
95 |
+
except Exception as e:
|
96 |
+
print(f"Error retrieving inventory items: {e}")
|
97 |
+
return []
|
98 |
+
|
99 |
+
def delete_inventory_item(self, item_id):
|
100 |
+
"""
|
101 |
+
Delete an inventory item by its ID.
|
102 |
+
|
103 |
+
:param item_id: The ID of the inventory item to be deleted.
|
104 |
+
:return: Boolean indicating whether the deletion was successful.
|
105 |
+
"""
|
106 |
+
try:
|
107 |
+
result = self.collection.delete_one({"_id": ObjectId(item_id)})
|
108 |
+
return result.deleted_count > 0
|
109 |
+
except Exception as e:
|
110 |
+
print(f"Error deleting inventory item: {e}")
|
111 |
+
return False
|
services/llama_service.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
2 |
+
|
3 |
+
# Load Llama 3.2 model and tokenizer for text generation
|
4 |
+
model_name = "meta-llama/Llama-3.2-1B"
|
5 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
6 |
+
model = AutoModelForCausalLM.from_pretrained(model_name)
|
7 |
+
|
8 |
+
# Set the padding token to EOS token or add a new padding token
|
9 |
+
if tokenizer.pad_token is None:
|
10 |
+
tokenizer.add_special_tokens({'pad_token': '[PAD]'})
|
11 |
+
model.resize_token_embeddings(len(tokenizer))
|
12 |
+
|
13 |
+
|
14 |
+
def llama2_response(user_input):
|
15 |
+
try:
|
16 |
+
inputs = tokenizer(user_input, return_tensors="pt", truncation=True, max_length=150, padding=True)
|
17 |
+
outputs = model.generate(
|
18 |
+
inputs["input_ids"],
|
19 |
+
max_length=150,
|
20 |
+
do_sample=True,
|
21 |
+
temperature=0.7,
|
22 |
+
pad_token_id=tokenizer.pad_token_id,
|
23 |
+
attention_mask=inputs["attention_mask"]
|
24 |
+
)
|
25 |
+
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
26 |
+
return response
|
27 |
+
except Exception as e:
|
28 |
+
return f"Error generating response: {str(e)}"
|
services/report_generator.py
CHANGED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
import pandas as pd
|
4 |
+
import datetime
|
5 |
+
|
6 |
+
# MongoDB Setup (import from your db configuration)
|
7 |
+
from config.db import db
|
8 |
+
|
9 |
+
# Define collections for report generation
|
10 |
+
inventory_collection = db.inventory
|
11 |
+
health_collection = db.health_records
|
12 |
+
usage_collection = db.user_usage
|
13 |
+
|
14 |
+
|
15 |
+
class ReportGenerator:
|
16 |
+
"""
|
17 |
+
Report Generator Service for generating reports on inventory, health, and assistant usage data.
|
18 |
+
"""
|
19 |
+
|
20 |
+
def __init__(self):
|
21 |
+
self.inventory_collection = inventory_collection
|
22 |
+
self.health_collection = health_collection
|
23 |
+
self.usage_collection = usage_collection
|
24 |
+
|
25 |
+
def generate_inventory_report(self, user_id=None):
|
26 |
+
"""
|
27 |
+
Generates a report of all inventory items, optionally filtered by user.
|
28 |
+
|
29 |
+
:param user_id: Optional. The ID of the user to filter the inventory report.
|
30 |
+
:return: A pandas DataFrame containing the inventory report.
|
31 |
+
"""
|
32 |
+
try:
|
33 |
+
query = {"user_id": ObjectId(user_id)} if user_id else {}
|
34 |
+
inventory_items = list(self.inventory_collection.find(query))
|
35 |
+
|
36 |
+
if not inventory_items:
|
37 |
+
return pd.DataFrame([])
|
38 |
+
|
39 |
+
# Convert the MongoDB documents to a pandas DataFrame
|
40 |
+
df = pd.DataFrame(inventory_items)
|
41 |
+
df = df.drop(columns=['_id', 'user_id']) # Drop internal fields
|
42 |
+
|
43 |
+
# Add a column for total value (if relevant for future use)
|
44 |
+
# df['total_value'] = df['quantity'] * df['unit_price']
|
45 |
+
|
46 |
+
return df
|
47 |
+
except Exception as e:
|
48 |
+
print(f"Error generating inventory report: {e}")
|
49 |
+
return pd.DataFrame([])
|
50 |
+
|
51 |
+
def generate_health_report(self, user_id=None):
|
52 |
+
"""
|
53 |
+
Generates a health report based on logged health records for poultry.
|
54 |
+
|
55 |
+
:param user_id: Optional. The ID of the user to filter the health report.
|
56 |
+
:return: A pandas DataFrame containing the health report.
|
57 |
+
"""
|
58 |
+
try:
|
59 |
+
query = {"user_id": ObjectId(user_id)} if user_id else {}
|
60 |
+
health_records = list(self.health_collection.find(query))
|
61 |
+
|
62 |
+
if not health_records:
|
63 |
+
return pd.DataFrame([])
|
64 |
+
|
65 |
+
# Convert the MongoDB documents to a pandas DataFrame
|
66 |
+
df = pd.DataFrame(health_records)
|
67 |
+
df = df.drop(columns=['_id', 'user_id'])
|
68 |
+
|
69 |
+
return df
|
70 |
+
except Exception as e:
|
71 |
+
print(f"Error generating health report: {e}")
|
72 |
+
return pd.DataFrame([])
|
73 |
+
|
74 |
+
def generate_usage_report(self, user_id=None, manager_id=None, interaction_type=None):
|
75 |
+
"""
|
76 |
+
Generates a report based on assistant usage data.
|
77 |
+
|
78 |
+
:param user_id: Optional. The ID of the user to filter the report.
|
79 |
+
:param manager_id: Optional. The ID of the manager to filter the report.
|
80 |
+
:param interaction_type: Optional. Filter by interaction type (e.g., "disease_detection").
|
81 |
+
:return: A pandas DataFrame containing the assistant usage report.
|
82 |
+
"""
|
83 |
+
try:
|
84 |
+
query = {}
|
85 |
+
if user_id:
|
86 |
+
query['user_id'] = ObjectId(user_id)
|
87 |
+
if manager_id:
|
88 |
+
query['manager_id'] = ObjectId(manager_id)
|
89 |
+
if interaction_type:
|
90 |
+
query['interaction_type'] = interaction_type
|
91 |
+
|
92 |
+
usage_records = list(self.usage_collection.find(query))
|
93 |
+
|
94 |
+
if not usage_records:
|
95 |
+
return pd.DataFrame([])
|
96 |
+
|
97 |
+
# Convert the MongoDB documents to a pandas DataFrame
|
98 |
+
df = pd.DataFrame(usage_records)
|
99 |
+
df = df.drop(columns=['_id', 'user_id', 'manager_id'])
|
100 |
+
|
101 |
+
return df
|
102 |
+
except Exception as e:
|
103 |
+
print(f"Error generating usage report: {e}")
|
104 |
+
return pd.DataFrame([])
|
105 |
+
|
106 |
+
def generate_combined_report(self, user_id=None, manager_id=None):
|
107 |
+
"""
|
108 |
+
Generates a combined report containing inventory, health, and usage data.
|
109 |
+
|
110 |
+
:param user_id: Optional. The ID of the user to filter all reports.
|
111 |
+
:param manager_id: Optional. The ID of the manager to filter usage report.
|
112 |
+
:return: A dictionary with three DataFrames for inventory, health, and usage reports.
|
113 |
+
"""
|
114 |
+
try:
|
115 |
+
inventory_report = self.generate_inventory_report(user_id)
|
116 |
+
health_report = self.generate_health_report(user_id)
|
117 |
+
usage_report = self.generate_usage_report(user_id=user_id, manager_id=manager_id)
|
118 |
+
|
119 |
+
return {
|
120 |
+
"inventory_report": inventory_report,
|
121 |
+
"health_report": health_report,
|
122 |
+
"usage_report": usage_report
|
123 |
+
}
|
124 |
+
except Exception as e:
|
125 |
+
print(f"Error generating combined report: {e}")
|
126 |
+
return {
|
127 |
+
"inventory_report": pd.DataFrame([]),
|
128 |
+
"health_report": pd.DataFrame([]),
|
129 |
+
"usage_report": pd.DataFrame([])
|
130 |
+
}
|
131 |
+
|
132 |
+
def export_report_to_csv(self, report_df, file_path):
|
133 |
+
"""
|
134 |
+
Exports a report (DataFrame) to a CSV file.
|
135 |
+
|
136 |
+
:param report_df: The pandas DataFrame containing the report data.
|
137 |
+
:param file_path: The file path to save the CSV file.
|
138 |
+
:return: Boolean indicating success or failure of the export.
|
139 |
+
"""
|
140 |
+
try:
|
141 |
+
report_df.to_csv(file_path, index=False)
|
142 |
+
return True
|
143 |
+
except Exception as e:
|
144 |
+
print(f"Error exporting report to CSV: {e}")
|
145 |
+
return False
|
services/usage_tracker.py
CHANGED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pymongo import MongoClient
|
2 |
+
from bson.objectid import ObjectId
|
3 |
+
import datetime
|
4 |
+
|
5 |
+
# MongoDB Setup (import from your db configuration)
|
6 |
+
from config.db import db
|
7 |
+
|
8 |
+
# Define the usage tracking collection
|
9 |
+
usage_collection = db.user_usage # MongoDB collection to store assistant usage logs
|
10 |
+
|
11 |
+
|
12 |
+
class UsageTracker:
|
13 |
+
"""
|
14 |
+
Usage Tracker Service for logging and retrieving assistant usage.
|
15 |
+
"""
|
16 |
+
|
17 |
+
def __init__(self):
|
18 |
+
self.collection = usage_collection
|
19 |
+
|
20 |
+
def log_usage(self, user_id, manager_id, interaction_type, metadata=None):
|
21 |
+
"""
|
22 |
+
Logs an interaction with the assistant.
|
23 |
+
|
24 |
+
:param user_id: The ID of the user interacting with the assistant.
|
25 |
+
:param manager_id: The ID of the user's manager.
|
26 |
+
:param interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
27 |
+
:param metadata: Optional. Additional details related to the interaction (e.g., query details, result).
|
28 |
+
:return: The inserted usage log's ID (MongoDB ObjectId).
|
29 |
+
"""
|
30 |
+
try:
|
31 |
+
log_entry = {
|
32 |
+
"user_id": ObjectId(user_id),
|
33 |
+
"manager_id": ObjectId(manager_id),
|
34 |
+
"interaction_type": interaction_type,
|
35 |
+
"metadata": metadata if metadata else {},
|
36 |
+
"timestamp": datetime.datetime.utcnow()
|
37 |
+
}
|
38 |
+
result = self.collection.insert_one(log_entry)
|
39 |
+
return str(result.inserted_id)
|
40 |
+
except Exception as e:
|
41 |
+
print(f"Error logging usage: {e}")
|
42 |
+
return None
|
43 |
+
|
44 |
+
def get_usage_by_user(self, user_id):
|
45 |
+
"""
|
46 |
+
Retrieve all assistant usage logs for a specific user.
|
47 |
+
|
48 |
+
:param user_id: The ID of the user.
|
49 |
+
:return: A list of usage logs for the user or an empty list if none are found.
|
50 |
+
"""
|
51 |
+
try:
|
52 |
+
return list(self.collection.find({"user_id": ObjectId(user_id)}))
|
53 |
+
except Exception as e:
|
54 |
+
print(f"Error retrieving usage logs for user: {e}")
|
55 |
+
return []
|
56 |
+
|
57 |
+
def get_usage_by_manager(self, manager_id):
|
58 |
+
"""
|
59 |
+
Retrieve all assistant usage logs for users managed by a specific manager.
|
60 |
+
|
61 |
+
:param manager_id: The ID of the manager.
|
62 |
+
:return: A list of usage logs for the manager's users or an empty list if none are found.
|
63 |
+
"""
|
64 |
+
try:
|
65 |
+
return list(self.collection.find({"manager_id": ObjectId(manager_id)}))
|
66 |
+
except Exception as e:
|
67 |
+
print(f"Error retrieving usage logs for manager: {e}")
|
68 |
+
return []
|
69 |
+
|
70 |
+
def get_usage_by_interaction_type(self, interaction_type):
|
71 |
+
"""
|
72 |
+
Retrieve all assistant usage logs for a specific interaction type.
|
73 |
+
|
74 |
+
:param interaction_type: The type of interaction (e.g., "disease_detection", "inventory_management").
|
75 |
+
:return: A list of usage logs for the interaction type or an empty list if none are found.
|
76 |
+
"""
|
77 |
+
try:
|
78 |
+
return list(self.collection.find({"interaction_type": interaction_type}))
|
79 |
+
except Exception as e:
|
80 |
+
print(f"Error retrieving usage logs by interaction type: {e}")
|
81 |
+
return []
|
82 |
+
|
83 |
+
def get_usage_logs(self, user_id=None, manager_id=None, interaction_type=None):
|
84 |
+
"""
|
85 |
+
Retrieves assistant usage logs, optionally filtered by user, manager, and/or interaction type.
|
86 |
+
|
87 |
+
:param user_id: Optional. Filter by user ID.
|
88 |
+
:param manager_id: Optional. Filter by manager ID.
|
89 |
+
:param interaction_type: Optional. Filter by interaction type.
|
90 |
+
:return: A list of usage logs or an empty list if none are found.
|
91 |
+
"""
|
92 |
+
try:
|
93 |
+
query = {}
|
94 |
+
if user_id:
|
95 |
+
query["user_id"] = ObjectId(user_id)
|
96 |
+
if manager_id:
|
97 |
+
query["manager_id"] = ObjectId(manager_id)
|
98 |
+
if interaction_type:
|
99 |
+
query["interaction_type"] = interaction_type
|
100 |
+
|
101 |
+
return list(self.collection.find(query))
|
102 |
+
except Exception as e:
|
103 |
+
print(f"Error retrieving usage logs: {e}")
|
104 |
+
return []
|
105 |
+
|
106 |
+
def delete_usage_log(self, log_id):
|
107 |
+
"""
|
108 |
+
Deletes a specific usage log by its ID.
|
109 |
+
|
110 |
+
:param log_id: The ID of the usage log to be deleted.
|
111 |
+
:return: Boolean indicating whether the deletion was successful.
|
112 |
+
"""
|
113 |
+
try:
|
114 |
+
result = self.collection.delete_one({"_id": ObjectId(log_id)})
|
115 |
+
return result.deleted_count > 0
|
116 |
+
except Exception as e:
|
117 |
+
print(f"Error deleting usage log: {e}")
|
118 |
+
return False
|
static/assets/css/gpt.css
ADDED
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Import Google font - Poppins */
|
2 |
+
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');
|
3 |
+
|
4 |
+
* {
|
5 |
+
margin: 0;
|
6 |
+
padding: 0;
|
7 |
+
box-sizing: border-box;
|
8 |
+
font-family: "Poppins", sans-serif;
|
9 |
+
}
|
10 |
+
|
11 |
+
:root {
|
12 |
+
--text-color: #FFFFFF;
|
13 |
+
--icon-color: #ACACBE;
|
14 |
+
--icon-hover-bg: #5b5e71;
|
15 |
+
--placeholder-color: #dcdcdc;
|
16 |
+
--outgoing-chat-bg: #343541;
|
17 |
+
--incoming-chat-bg: #444654;
|
18 |
+
--outgoing-chat-border: #343541;
|
19 |
+
--incoming-chat-border: #444654;
|
20 |
+
}
|
21 |
+
|
22 |
+
.light-mode {
|
23 |
+
--text-color: #343541;
|
24 |
+
--icon-color: #a9a9bc;
|
25 |
+
--icon-hover-bg: #f1f1f3;
|
26 |
+
--placeholder-color: #6c6c6c;
|
27 |
+
--outgoing-chat-bg: #FFFFFF;
|
28 |
+
--incoming-chat-bg: #F7F7F8;
|
29 |
+
--outgoing-chat-border: #FFFFFF;
|
30 |
+
--incoming-chat-border: #D9D9E3;
|
31 |
+
}
|
32 |
+
|
33 |
+
body {
|
34 |
+
background: var(--outgoing-chat-bg);
|
35 |
+
}
|
36 |
+
|
37 |
+
/* Chats container styling */
|
38 |
+
.chat-container {
|
39 |
+
overflow-y: auto;
|
40 |
+
max-height: 100vh;
|
41 |
+
padding-bottom: 150px;
|
42 |
+
}
|
43 |
+
|
44 |
+
:where(.chat-container, textarea)::-webkit-scrollbar {
|
45 |
+
width: 6px;
|
46 |
+
}
|
47 |
+
|
48 |
+
:where(.chat-container, textarea)::-webkit-scrollbar-track {
|
49 |
+
background: var(--incoming-chat-bg);
|
50 |
+
border-radius: 25px;
|
51 |
+
}
|
52 |
+
|
53 |
+
:where(.chat-container, textarea)::-webkit-scrollbar-thumb {
|
54 |
+
background: var(--icon-color);
|
55 |
+
border-radius: 25px;
|
56 |
+
}
|
57 |
+
|
58 |
+
.default-text {
|
59 |
+
display: flex;
|
60 |
+
align-items: center;
|
61 |
+
justify-content: center;
|
62 |
+
flex-direction: column;
|
63 |
+
height: 70vh;
|
64 |
+
padding: 0 10px;
|
65 |
+
text-align: center;
|
66 |
+
color: var(--text-color);
|
67 |
+
}
|
68 |
+
|
69 |
+
.default-text h1 {
|
70 |
+
font-size: 3.3rem;
|
71 |
+
}
|
72 |
+
|
73 |
+
.default-text p {
|
74 |
+
margin-top: 10px;
|
75 |
+
font-size: 1.1rem;
|
76 |
+
}
|
77 |
+
|
78 |
+
.chat-container .chat {
|
79 |
+
padding: 25px 10px;
|
80 |
+
display: flex;
|
81 |
+
justify-content: center;
|
82 |
+
color: var(--text-color);
|
83 |
+
}
|
84 |
+
|
85 |
+
.chat-container .chat.outgoing {
|
86 |
+
background: var(--outgoing-chat-bg);
|
87 |
+
border: 1px solid var(--outgoing-chat-border);
|
88 |
+
}
|
89 |
+
|
90 |
+
.chat-container .chat.incoming {
|
91 |
+
background: var(--incoming-chat-bg);
|
92 |
+
border: 1px solid var(--incoming-chat-border);
|
93 |
+
}
|
94 |
+
|
95 |
+
.chat .chat-content {
|
96 |
+
display: flex;
|
97 |
+
max-width: 1200px;
|
98 |
+
width: 100%;
|
99 |
+
align-items: flex-start;
|
100 |
+
justify-content: space-between;
|
101 |
+
}
|
102 |
+
|
103 |
+
span.material-symbols-rounded {
|
104 |
+
user-select: none;
|
105 |
+
cursor: pointer;
|
106 |
+
}
|
107 |
+
|
108 |
+
.chat .chat-content span {
|
109 |
+
cursor: pointer;
|
110 |
+
font-size: 1.3rem;
|
111 |
+
color: var(--icon-color);
|
112 |
+
visibility: hidden;
|
113 |
+
}
|
114 |
+
|
115 |
+
.chat:hover .chat-content:not(:has(.typing-animation), :has(.error)) span {
|
116 |
+
visibility: visible;
|
117 |
+
}
|
118 |
+
|
119 |
+
.chat .chat-details {
|
120 |
+
display: flex;
|
121 |
+
align-items: center;
|
122 |
+
}
|
123 |
+
|
124 |
+
.chat .chat-details img {
|
125 |
+
width: 35px;
|
126 |
+
height: 35px;
|
127 |
+
align-self: flex-start;
|
128 |
+
object-fit: cover;
|
129 |
+
border-radius: 2px;
|
130 |
+
}
|
131 |
+
|
132 |
+
.chat .chat-details p {
|
133 |
+
white-space: pre-wrap;
|
134 |
+
font-size: 1.05rem;
|
135 |
+
padding: 0 50px 0 25px;
|
136 |
+
color: var(--text-color);
|
137 |
+
word-break: break-word;
|
138 |
+
}
|
139 |
+
|
140 |
+
.chat .chat-details p.error {
|
141 |
+
color: #e55865;
|
142 |
+
}
|
143 |
+
|
144 |
+
.chat .typing-animation {
|
145 |
+
padding-left: 25px;
|
146 |
+
display: inline-flex;
|
147 |
+
}
|
148 |
+
|
149 |
+
.typing-animation .typing-dot {
|
150 |
+
height: 7px;
|
151 |
+
width: 7px;
|
152 |
+
border-radius: 50%;
|
153 |
+
margin: 0 3px;
|
154 |
+
opacity: 0.7;
|
155 |
+
background: var(--text-color);
|
156 |
+
animation: animateDots 1.5s var(--delay) ease-in-out infinite;
|
157 |
+
}
|
158 |
+
|
159 |
+
.typing-animation .typing-dot:first-child {
|
160 |
+
margin-left: 0;
|
161 |
+
}
|
162 |
+
|
163 |
+
@keyframes animateDots {
|
164 |
+
0%, 44% {
|
165 |
+
transform: translateY(0px);
|
166 |
+
}
|
167 |
+
28% {
|
168 |
+
opacity: 0.4;
|
169 |
+
transform: translateY(-6px);
|
170 |
+
}
|
171 |
+
44% {
|
172 |
+
opacity: 0.2;
|
173 |
+
}
|
174 |
+
}
|
175 |
+
|
176 |
+
/* Typing container styling */
|
177 |
+
.typing-container {
|
178 |
+
position: fixed;
|
179 |
+
bottom: 0;
|
180 |
+
width: 100%;
|
181 |
+
display: flex;
|
182 |
+
padding: 20px 10px;
|
183 |
+
justify-content: center;
|
184 |
+
background: var(--outgoing-chat-bg);
|
185 |
+
border-top: 1px solid var(--incoming-chat-border);
|
186 |
+
}
|
187 |
+
|
188 |
+
.typing-container .typing-content {
|
189 |
+
display: flex;
|
190 |
+
max-width: 950px;
|
191 |
+
width: 100%;
|
192 |
+
align-items: flex-end;
|
193 |
+
}
|
194 |
+
|
195 |
+
.typing-container .typing-textarea {
|
196 |
+
width: 100%;
|
197 |
+
display: flex;
|
198 |
+
position: relative;
|
199 |
+
}
|
200 |
+
|
201 |
+
.typing-textarea textarea {
|
202 |
+
resize: none;
|
203 |
+
height: 55px;
|
204 |
+
width: 100%;
|
205 |
+
border: none;
|
206 |
+
padding: 15px 45px 15px 20px;
|
207 |
+
color: var(--text-color);
|
208 |
+
font-size: 1rem;
|
209 |
+
border-radius: 4px;
|
210 |
+
max-height: 250px;
|
211 |
+
overflow-y: auto;
|
212 |
+
background: var(--incoming-chat-bg);
|
213 |
+
outline: 1px solid var(--incoming-chat-border);
|
214 |
+
}
|
215 |
+
|
216 |
+
.typing-textarea textarea::placeholder {
|
217 |
+
color: var(--placeholder-color);
|
218 |
+
}
|
219 |
+
|
220 |
+
.typing-content span {
|
221 |
+
width: 55px;
|
222 |
+
height: 55px;
|
223 |
+
display: flex;
|
224 |
+
border-radius: 4px;
|
225 |
+
font-size: 1.35rem;
|
226 |
+
align-items: center;
|
227 |
+
justify-content: center;
|
228 |
+
color: var(--icon-color);
|
229 |
+
}
|
230 |
+
|
231 |
+
.typing-textarea span {
|
232 |
+
position: absolute;
|
233 |
+
right: 0;
|
234 |
+
bottom: 0;
|
235 |
+
visibility: hidden;
|
236 |
+
}
|
237 |
+
|
238 |
+
.typing-textarea textarea:valid ~ span {
|
239 |
+
visibility: visible;
|
240 |
+
}
|
241 |
+
|
242 |
+
.typing-controls {
|
243 |
+
display: flex;
|
244 |
+
}
|
245 |
+
|
246 |
+
.typing-controls span {
|
247 |
+
margin-left: 7px;
|
248 |
+
font-size: 1.4rem;
|
249 |
+
background: var(--incoming-chat-bg);
|
250 |
+
outline: 1px solid var(--incoming-chat-border);
|
251 |
+
}
|
252 |
+
|
253 |
+
.typing-controls span:hover {
|
254 |
+
background: var(--icon-hover-bg);
|
255 |
+
}
|
256 |
+
|
257 |
+
/* Reponsive Media Query */
|
258 |
+
@media screen and (max-width: 600px) {
|
259 |
+
.default-text h1 {
|
260 |
+
font-size: 2.3rem;
|
261 |
+
}
|
262 |
+
|
263 |
+
:where(.default-text p, textarea, .chat p) {
|
264 |
+
font-size: 0.95rem !important;
|
265 |
+
}
|
266 |
+
|
267 |
+
.chat-container .chat {
|
268 |
+
padding: 20px 10px;
|
269 |
+
}
|
270 |
+
|
271 |
+
.chat-container .chat img {
|
272 |
+
height: 32px;
|
273 |
+
width: 32px;
|
274 |
+
}
|
275 |
+
|
276 |
+
.chat-container .chat p {
|
277 |
+
padding: 0 20px;
|
278 |
+
}
|
279 |
+
|
280 |
+
.chat .chat-content:not(:has(.typing-animation), :has(.error)) span {
|
281 |
+
visibility: visible;
|
282 |
+
}
|
283 |
+
|
284 |
+
.typing-container {
|
285 |
+
padding: 15px 10px;
|
286 |
+
}
|
287 |
+
|
288 |
+
.typing-textarea textarea {
|
289 |
+
height: 45px;
|
290 |
+
padding: 10px 40px 10px 10px;
|
291 |
+
}
|
292 |
+
|
293 |
+
.typing-content span {
|
294 |
+
height: 45px;
|
295 |
+
width: 45px;
|
296 |
+
margin-left: 5px;
|
297 |
+
}
|
298 |
+
|
299 |
+
span.material-symbols-rounded {
|
300 |
+
font-size: 1.25rem !important;
|
301 |
+
}
|
302 |
+
}
|
static/assets/{css/style.css → images/__init__.py}
RENAMED
File without changes
|
static/assets/images/chatbot.jpg
ADDED
![]() |
Git LFS Details
|
static/assets/js/app.js
DELETED
File without changes
|
static/assets/js/gpt.js
ADDED
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const chatInput = document.querySelector("#user-input");
|
2 |
+
const sendButton = document.querySelector("#send-btn");
|
3 |
+
const chatContainer = document.querySelector("#chat-box");
|
4 |
+
const themeButton = document.querySelector("#theme-btn");
|
5 |
+
const deleteButton = document.querySelector("#delete-btn");
|
6 |
+
|
7 |
+
let userText = null;
|
8 |
+
|
9 |
+
const loadDataFromLocalstorage = () => {
|
10 |
+
// Load saved chats and theme from local storage and apply/add on the page
|
11 |
+
const themeColor = localStorage.getItem("themeColor");
|
12 |
+
|
13 |
+
document.body.classList.toggle("light-mode", themeColor === "light_mode");
|
14 |
+
themeButton.innerText = document.body.classList.contains("light-mode") ? "dark_mode" : "light_mode";
|
15 |
+
|
16 |
+
const defaultText = `<div class="default-text">
|
17 |
+
<h1>Poultry Management Assistant</h1>
|
18 |
+
<p>Start a conversation about poultry management.<br> Your chat history will be displayed here.</p>
|
19 |
+
</div>`;
|
20 |
+
|
21 |
+
chatContainer.innerHTML = localStorage.getItem("all-chats") || defaultText;
|
22 |
+
chatContainer.scrollTo(0, chatContainer.scrollHeight); // Scroll to bottom of the chat container
|
23 |
+
}
|
24 |
+
|
25 |
+
const createChatElement = (content, className) => {
|
26 |
+
// Create new div and apply chat, specified class and set html content of div
|
27 |
+
const chatDiv = document.createElement("div");
|
28 |
+
chatDiv.classList.add("chat", className);
|
29 |
+
chatDiv.innerHTML = content;
|
30 |
+
return chatDiv; // Return the created chat div
|
31 |
+
}
|
32 |
+
|
33 |
+
const getChatResponse = async (incomingChatDiv) => {
|
34 |
+
const API_URL = "/api/chat";
|
35 |
+
const pElement = document.createElement("p");
|
36 |
+
|
37 |
+
// Define the properties and data for the API request
|
38 |
+
const requestOptions = {
|
39 |
+
method: "POST",
|
40 |
+
headers: {
|
41 |
+
"Content-Type": "application/json"
|
42 |
+
},
|
43 |
+
body: JSON.stringify({
|
44 |
+
message: userText,
|
45 |
+
})
|
46 |
+
}
|
47 |
+
|
48 |
+
// Send POST request to API, get response and set the response as paragraph element text
|
49 |
+
try {
|
50 |
+
const response = await (await fetch(API_URL, requestOptions)).json();
|
51 |
+
pElement.textContent = response.response.trim();
|
52 |
+
} catch (error) {
|
53 |
+
// Add error class to the paragraph element and set error text
|
54 |
+
pElement.classList.add("error");
|
55 |
+
pElement.textContent = "Oops! Something went wrong while retrieving the response. Please try again.";
|
56 |
+
}
|
57 |
+
|
58 |
+
// Remove the typing animation, append the paragraph element and save the chats to local storage
|
59 |
+
incomingChatDiv.querySelector(".typing-animation").remove();
|
60 |
+
incomingChatDiv.querySelector(".chat-details").appendChild(pElement);
|
61 |
+
localStorage.setItem("all-chats", chatContainer.innerHTML);
|
62 |
+
chatContainer.scrollTo(0, chatContainer.scrollHeight);
|
63 |
+
}
|
64 |
+
|
65 |
+
const showTypingAnimation = () => {
|
66 |
+
// Display the typing animation and call the getChatResponse function
|
67 |
+
const html = `<div class="chat-content">
|
68 |
+
<div class="chat-details">
|
69 |
+
<img src="/static/assets/images/chatbot.jpg" alt="chatbot-img">
|
70 |
+
<div class="typing-animation">
|
71 |
+
<div class="typing-dot" style="--delay: 0.2s"></div>
|
72 |
+
<div class="typing-dot" style="--delay: 0.3s"></div>
|
73 |
+
<div class="typing-dot" style="--delay: 0.4s"></div>
|
74 |
+
</div>
|
75 |
+
</div>
|
76 |
+
<span onclick="copyResponse(this)" class="material-symbols-rounded">content_copy</span>
|
77 |
+
</div>`;
|
78 |
+
// Create an incoming chat div with typing animation and append it to chat container
|
79 |
+
const incomingChatDiv = createChatElement(html, "incoming");
|
80 |
+
chatContainer.appendChild(incomingChatDiv);
|
81 |
+
chatContainer.scrollTo(0, chatContainer.scrollHeight);
|
82 |
+
getChatResponse(incomingChatDiv);
|
83 |
+
}
|
84 |
+
|
85 |
+
const handleOutgoingChat = () => {
|
86 |
+
userText = chatInput.value.trim(); // Get chatInput value and remove extra spaces
|
87 |
+
if (!userText) return; // If chatInput is empty return from here
|
88 |
+
|
89 |
+
// Clear the input field and reset its height
|
90 |
+
chatInput.value = "";
|
91 |
+
chatInput.style.height = `${initialInputHeight}px`;
|
92 |
+
|
93 |
+
const html = `<div class="chat-content">
|
94 |
+
<div class="chat-details">
|
95 |
+
<img src="/static/assets/images/user.jpg" alt="user-img">
|
96 |
+
<p>${userText}</p>
|
97 |
+
</div>
|
98 |
+
</div>`;
|
99 |
+
|
100 |
+
// Create an outgoing chat div with user's message and append it to chat container
|
101 |
+
const outgoingChatDiv = createChatElement(html, "outgoing");
|
102 |
+
chatContainer.querySelector(".default-text")?.remove();
|
103 |
+
chatContainer.appendChild(outgoingChatDiv);
|
104 |
+
chatContainer.scrollTo(0, chatContainer.scrollHeight);
|
105 |
+
setTimeout(showTypingAnimation, 500);
|
106 |
+
}
|
107 |
+
|
108 |
+
deleteButton.addEventListener("click", () => {
|
109 |
+
// Remove the chats from local storage and call loadDataFromLocalstorage function
|
110 |
+
if (confirm("Are you sure you want to delete all the chats?")) {
|
111 |
+
localStorage.removeItem("all-chats");
|
112 |
+
loadDataFromLocalstorage();
|
113 |
+
}
|
114 |
+
});
|
115 |
+
|
116 |
+
themeButton.addEventListener("click", () => {
|
117 |
+
// Toggle body's class for the theme mode and save the updated theme to the local storage
|
118 |
+
document.body.classList.toggle("light-mode");
|
119 |
+
localStorage.setItem("themeColor", themeButton.innerText);
|
120 |
+
themeButton.innerText = document.body.classList.contains("light-mode") ? "dark_mode" : "light_mode";
|
121 |
+
});
|
122 |
+
|
123 |
+
const initialInputHeight = chatInput.scrollHeight;
|
124 |
+
|
125 |
+
chatInput.addEventListener("input", () => {
|
126 |
+
// Adjust the height of the input field dynamically based on its content
|
127 |
+
chatInput.style.height = `${initialInputHeight}px`;
|
128 |
+
chatInput.style.height = `${chatInput.scrollHeight}px`;
|
129 |
+
});
|
130 |
+
|
131 |
+
chatInput.addEventListener("keydown", (e) => {
|
132 |
+
// If the Enter key is pressed without Shift, handle the outgoing chat
|
133 |
+
if (e.key === "Enter" && !e.shiftKey) {
|
134 |
+
e.preventDefault();
|
135 |
+
handleOutgoingChat();
|
136 |
+
}
|
137 |
+
});
|
138 |
+
|
139 |
+
loadDataFromLocalstorage();
|
140 |
+
sendButton.addEventListener("click", handleOutgoingChat);
|
templates/index.html
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html dir="ltr" lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
6 |
+
<title>Poultry Management Assistant</title>
|
7 |
+
<!-- Google Fonts Link For Icons -->
|
8 |
+
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,[email protected],100..700,0..1,-50..200"
|
9 |
+
rel="stylesheet"/>
|
10 |
+
<link href="/static/assets/css/gpt.css" rel="stylesheet">
|
11 |
+
</head>
|
12 |
+
<body>
|
13 |
+
<!-- Chats container -->
|
14 |
+
<div class="chat-container" id="chat-box"></div>
|
15 |
+
|
16 |
+
<!-- Typing container -->
|
17 |
+
<div class="typing-container">
|
18 |
+
<div class="typing-content">
|
19 |
+
<div class="typing-textarea">
|
20 |
+
<input accept="image/*" id="image-input" style="display: none;" type="file">
|
21 |
+
<textarea id="user-input" placeholder="Enter a prompt here" required spellcheck="false"></textarea>
|
22 |
+
<span class="material-symbols-rounded" id="send-btn">send</span>
|
23 |
+
</div>
|
24 |
+
<div class="typing-controls">
|
25 |
+
<span class="material-symbols-rounded" id="theme-btn">light_mode</span>
|
26 |
+
<span class="material-symbols-rounded" id="delete-btn">delete</span>
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
</div>
|
30 |
+
|
31 |
+
<script src="/static/assets/js/gpt.js"></script>
|
32 |
+
</body>
|
33 |
+
</html>
|