Emmanuel Frimpong Asante commited on
Commit
6513666
·
1 Parent(s): 4a66159

"Update system"

Browse files

Signed-off-by: Emmanuel Frimpong Asante <[email protected]>

.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="inheritedJdk" />
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="Python 3.13 (Poultry_Diseases_Detector)" project-jdk-type="Python SDK" />
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 transformers import AutoModelForCausalLM, AutoTokenizer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 (for inventory, record-keeping, etc.)
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
- # Load TensorFlow/Keras models with GPU support if available, otherwise use CPU
38
- try:
39
- device_name = '/GPU:0' if len(tf.config.list_physical_devices('GPU')) > 0 else '/CPU:0'
40
- with tf.device(device_name):
41
- my_model = load_model('models/Final_Chicken_disease_model.h5', compile=True)
42
- auth_model = load_model('models/auth_model.h5', compile=True)
43
- print(f"Models loaded successfully on {device_name}.")
44
- except Exception as e:
45
- print(f"Error loading models: {e}")
46
-
47
- # Updated Disease names and recommendations based on fecal analysis
48
- name_disease = {0: 'Coccidiosis', 1: 'Healthy', 2: 'New Castle Disease', 3: 'Salmonella'}
49
- result = {0: 'Critical', 1: 'No issue', 2: 'Critical', 3: 'Critical'}
50
- recommend = {
51
- 0: 'Panadol',
52
- 1: 'You have no need Medicine',
53
- 2: 'Percetamol',
54
- 3: 'Ponston'
55
- }
56
-
57
- class PoultryFarmBot:
58
- def __init__(self):
59
- self.db = db # MongoDB database for future use
60
-
61
- # Image Preprocessing for Fecal Disease Detection
62
- def preprocess_image(self, image):
63
- try:
64
- image_check = cv2.resize(image, (224, 224))
65
- image_check = np.expand_dims(image_check, axis=0) # Add batch dimension
66
- return image_check
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
- chatbot_interface.queue().launch(debug=True, share=True)
 
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
- keras~=2.12.0
2
- gradio~=4.40.0
3
- opencv-python~=4.10.0.84
4
- tensorflow==2.12.0
5
- transformers~=4.43.3
6
- numpy~=1.23.5
7
  torchvision
 
 
 
 
8
  accelerate
9
- openai==0.28
 
 
 
 
 
10
  pymongo
11
- torch
 
 
 
 
 
 
 
 
 
 
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

  • SHA256: cdd638a66baba3ea4de7e7ad5a8138b80ba13a0c4ec54487302c6c687c6e8f14
  • Pointer size: 130 Bytes
  • Size of remote file: 15.3 kB
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>