Emmanuel Frimpong Asante commited on
Commit
e748017
·
1 Parent(s): ebe0f8b

update space

Browse files
.idea/workspace.xml CHANGED
@@ -4,7 +4,16 @@
4
  <option name="autoReloadType" value="SELECTIVE" />
5
  </component>
6
  <component name="ChangeListManager">
7
- <list default="true" id="27c9ae1a-a6fa-4472-8bcd-a7087620894b" name="Changes" comment="update space" />
 
 
 
 
 
 
 
 
 
8
  <option name="SHOW_DIALOG" value="false" />
9
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
  <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -14,10 +23,10 @@
14
  <option name="RECENT_TEMPLATES">
15
  <list>
16
  <option value="Dockerfile" />
17
- <option value="JavaScript File" />
18
- <option value="CSS File" />
19
  <option value="HTML File" />
20
  <option value="Python Script" />
 
 
21
  </list>
22
  </option>
23
  </component>
@@ -38,25 +47,25 @@
38
  <option name="hideEmptyMiddlePackages" value="true" />
39
  <option name="showLibraryContents" value="true" />
40
  </component>
41
- <component name="PropertiesComponent">{
42
- &quot;keyToString&quot;: {
43
- &quot;ASKED_SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
44
- &quot;DefaultHtmlFileTemplate&quot;: &quot;HTML File&quot;,
45
- &quot;FastAPI.Unnamed.executor&quot;: &quot;Run&quot;,
46
- &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
47
- &quot;git-widget-placeholder&quot;: &quot;main&quot;,
48
- &quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
49
- &quot;last_opened_file_path&quot;: &quot;C:/git/Generative_AI_with_poultry_disease_detection_system_v2/models&quot;,
50
- &quot;list.type.of.created.stylesheet&quot;: &quot;CSS&quot;,
51
- &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
52
- &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
53
- &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
54
- &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
55
- &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
56
- &quot;settings.editor.selected.configurable&quot;: &quot;swagger&quot;,
57
- &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
58
  }
59
- }</component>
60
  <component name="RecentsManager">
61
  <key name="CopyFile.RECENT_KEYS">
62
  <recent name="C:\git\Generative_AI_with_poultry_disease_detection_system_v2\models" />
@@ -75,14 +84,14 @@
75
  </component>
76
  <component name="RunManager">
77
  <configuration name="Unnamed" type="Python.FastAPI" nameIsGenerated="true">
78
- <option name="file" value="C:\git\Generative_AI_with_poultry_disease_detection_system_v2\app.py" />
79
  <module name="Generative_AI_with_poultry_disease_detection_system_v2" />
80
  <option name="ENV_FILES" value="" />
81
  <option name="INTERPRETER_OPTIONS" value="" />
82
  <option name="PARENT_ENVS" value="true" />
83
- <option name="SDK_HOME" value="$PROJECT_DIR$/.venv/Scripts/python.exe" />
84
  <option name="WORKING_DIRECTORY" value="" />
85
- <option name="IS_MODULE_SDK" value="false" />
86
  <option name="ADD_CONTENT_ROOTS" value="true" />
87
  <option name="ADD_SOURCE_ROOTS" value="true" />
88
  <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
@@ -125,14 +134,6 @@
125
  <workItem from="1730454506390" duration="12672000" />
126
  <workItem from="1730494385249" duration="17097000" />
127
  </task>
128
- <task id="LOCAL-00104" summary="update space">
129
- <option name="closed" value="true" />
130
- <created>1730471112648</created>
131
- <option name="number" value="00104" />
132
- <option name="presentableId" value="LOCAL-00104" />
133
- <option name="project" value="LOCAL" />
134
- <updated>1730471112648</updated>
135
- </task>
136
  <task id="LOCAL-00105" summary="update space">
137
  <option name="closed" value="true" />
138
  <created>1730471205719</created>
@@ -517,7 +518,15 @@
517
  <option name="project" value="LOCAL" />
518
  <updated>1731737347454</updated>
519
  </task>
520
- <option name="localTasksCounter" value="153" />
 
 
 
 
 
 
 
 
521
  <servers />
522
  </component>
523
  <component name="TypeScriptGeneratedFilesManager">
@@ -541,6 +550,6 @@
541
  <option name="LAST_COMMIT_MESSAGE" value="update space" />
542
  </component>
543
  <component name="com.intellij.coverage.CoverageDataManagerImpl">
544
- <SUITE FILE_PATH="coverage/Generative_AI_with_poultry_disease_detection_system_v2$Unnamed.coverage" NAME="Unnamed Coverage Results" MODIFIED="1730337647536" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
545
  </component>
546
  </project>
 
4
  <option name="autoReloadType" value="SELECTIVE" />
5
  </component>
6
  <component name="ChangeListManager">
7
+ <list default="true" id="27c9ae1a-a6fa-4472-8bcd-a7087620894b" name="Changes" comment="update space">
8
+ <change afterPath="$PROJECT_DIR$/backend/app/static/css/chat.css" afterDir="false" />
9
+ <change afterPath="$PROJECT_DIR$/backend/app/static/js/chat.js" afterDir="false" />
10
+ <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
11
+ <change beforePath="$PROJECT_DIR$/backend/app/routes/chat.py" beforeDir="false" afterPath="$PROJECT_DIR$/backend/app/routes/chat.py" afterDir="false" />
12
+ <change beforePath="$PROJECT_DIR$/backend/app/routes/dashboard.py" beforeDir="false" afterPath="$PROJECT_DIR$/backend/app/routes/dashboard.py" afterDir="false" />
13
+ <change beforePath="$PROJECT_DIR$/backend/app/static/js/dashboard.js" beforeDir="false" afterPath="$PROJECT_DIR$/backend/app/static/js/dashboard.js" afterDir="false" />
14
+ <change beforePath="$PROJECT_DIR$/backend/app/templates/chat/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/backend/app/templates/chat/index.html" afterDir="false" />
15
+ <change beforePath="$PROJECT_DIR$/requirements.txt" beforeDir="false" afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
16
+ </list>
17
  <option name="SHOW_DIALOG" value="false" />
18
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
19
  <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
 
23
  <option name="RECENT_TEMPLATES">
24
  <list>
25
  <option value="Dockerfile" />
 
 
26
  <option value="HTML File" />
27
  <option value="Python Script" />
28
+ <option value="CSS File" />
29
+ <option value="JavaScript File" />
30
  </list>
31
  </option>
32
  </component>
 
47
  <option name="hideEmptyMiddlePackages" value="true" />
48
  <option name="showLibraryContents" value="true" />
49
  </component>
50
+ <component name="PropertiesComponent"><![CDATA[{
51
+ "keyToString": {
52
+ "ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
53
+ "DefaultHtmlFileTemplate": "HTML File",
54
+ "FastAPI.Unnamed.executor": "Run",
55
+ "RunOnceActivity.ShowReadmeOnStart": "true",
56
+ "git-widget-placeholder": "main",
57
+ "ignore.virus.scanning.warn.message": "true",
58
+ "last_opened_file_path": "C:/git/poultry/Generative_AI_with_poultry_disease_detection_system_v2/backend/app/main.py",
59
+ "list.type.of.created.stylesheet": "CSS",
60
+ "node.js.detected.package.eslint": "true",
61
+ "node.js.detected.package.tslint": "true",
62
+ "node.js.selected.package.eslint": "(autodetect)",
63
+ "node.js.selected.package.tslint": "(autodetect)",
64
+ "nodejs_package_manager_path": "npm",
65
+ "settings.editor.selected.configurable": "swagger",
66
+ "vue.rearranger.settings.migration": "true"
67
  }
68
+ }]]></component>
69
  <component name="RecentsManager">
70
  <key name="CopyFile.RECENT_KEYS">
71
  <recent name="C:\git\Generative_AI_with_poultry_disease_detection_system_v2\models" />
 
84
  </component>
85
  <component name="RunManager">
86
  <configuration name="Unnamed" type="Python.FastAPI" nameIsGenerated="true">
87
+ <option name="file" value="C:\git\poultry\Generative_AI_with_poultry_disease_detection_system_v2\backend\app\main.py" />
88
  <module name="Generative_AI_with_poultry_disease_detection_system_v2" />
89
  <option name="ENV_FILES" value="" />
90
  <option name="INTERPRETER_OPTIONS" value="" />
91
  <option name="PARENT_ENVS" value="true" />
92
+ <option name="SDK_HOME" value="" />
93
  <option name="WORKING_DIRECTORY" value="" />
94
+ <option name="IS_MODULE_SDK" value="true" />
95
  <option name="ADD_CONTENT_ROOTS" value="true" />
96
  <option name="ADD_SOURCE_ROOTS" value="true" />
97
  <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
 
134
  <workItem from="1730454506390" duration="12672000" />
135
  <workItem from="1730494385249" duration="17097000" />
136
  </task>
 
 
 
 
 
 
 
 
137
  <task id="LOCAL-00105" summary="update space">
138
  <option name="closed" value="true" />
139
  <created>1730471205719</created>
 
518
  <option name="project" value="LOCAL" />
519
  <updated>1731737347454</updated>
520
  </task>
521
+ <task id="LOCAL-00153" summary="update space">
522
+ <option name="closed" value="true" />
523
+ <created>1731738406662</created>
524
+ <option name="number" value="00153" />
525
+ <option name="presentableId" value="LOCAL-00153" />
526
+ <option name="project" value="LOCAL" />
527
+ <updated>1731738406662</updated>
528
+ </task>
529
+ <option name="localTasksCounter" value="154" />
530
  <servers />
531
  </component>
532
  <component name="TypeScriptGeneratedFilesManager">
 
550
  <option name="LAST_COMMIT_MESSAGE" value="update space" />
551
  </component>
552
  <component name="com.intellij.coverage.CoverageDataManagerImpl">
553
+ <SUITE FILE_PATH="coverage/Generative_AI_with_poultry_disease_detection_system_v2$Unnamed.coverage" NAME="Unnamed Coverage Results" MODIFIED="1731741961150" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
554
  </component>
555
  </project>
backend/app/routes/chat.py CHANGED
@@ -18,24 +18,22 @@ async def chat_with_ai(message: str, current_user: dict = Depends(get_current_us
18
  Process user messages using Llama-3.2 and return a response.
19
  """
20
  try:
21
- user_id = ObjectId(current_user["_id"])
22
- session_id = ObjectId()
23
 
24
  # Retrieve the latest chat session for context
25
  chat_session = db.chat_histories.find_one({"user_id": user_id}, sort=[("updated_at", -1)])
26
- session_id = chat_session["session_id"] if chat_session else session_id
27
 
28
  # Process the message with Llama-3.2
29
- response = process_message(user_id, session_id, message)
30
 
31
  # Save the chat to history
32
  chat_entry = {
33
- "user_id": user_id,
34
- "session_id": session_id,
35
  "user_message": message,
36
  "assistant_response": response,
37
  "timestamp": datetime.now(timezone.utc)
38
  }
 
39
  db.chat_histories.update_one(
40
  {"user_id": user_id, "session_id": session_id},
41
  {"$push": {"messages": chat_entry}, "$set": {"updated_at": datetime.now(timezone.utc)}},
@@ -44,25 +42,29 @@ async def chat_with_ai(message: str, current_user: dict = Depends(get_current_us
44
 
45
  return {"response": response}
46
  except Exception as e:
47
- raise HTTPException(status_code=500, detail="Error processing message.")
48
 
49
 
 
 
50
  @router.post("/upload")
51
  async def upload_image(file: UploadFile = File(...), current_user: dict = Depends(get_current_user)):
52
  """
53
  Upload an image for disease detection and return the analysis result.
54
  """
55
  try:
 
 
56
  # Read and analyze the image
57
  file_bytes = await file.read()
58
  analysis_result = detect_disease(file_bytes)
59
 
60
- if analysis_result["name"] == "Unknown Image":
61
  raise HTTPException(status_code=400, detail="Invalid image format.")
62
 
63
  # Save the analysis result to the database
64
  analysis_entry = {
65
- "user_id": ObjectId(current_user["_id"]),
66
  "file_name": file.filename,
67
  "analysis_result": analysis_result,
68
  "timestamp": datetime.now(timezone.utc)
@@ -71,8 +73,7 @@ async def upload_image(file: UploadFile = File(...), current_user: dict = Depend
71
 
72
  return {"analysis_result": analysis_result}
73
  except Exception as e:
74
- raise HTTPException(status_code=500, detail="Error analyzing image.")
75
-
76
 
77
  @router.get("/history")
78
  async def get_chat_history(current_user: dict = Depends(get_current_user)):
@@ -80,25 +81,30 @@ async def get_chat_history(current_user: dict = Depends(get_current_user)):
80
  Retrieve previous chat history for the logged-in user.
81
  """
82
  try:
83
- user_id = ObjectId(current_user["_id"])
84
  history = list(db.chat_histories.find({"user_id": user_id}).sort("updated_at", -1))
85
 
86
  # Convert ObjectId to string for JSON serialization
 
87
  for chat in history:
88
- chat["_id"] = str(chat["_id"])
89
- chat["session_id"] = str(chat["session_id"])
90
- chat["messages"] = [
91
- {
92
- "user_message": msg["user_message"],
93
- "assistant_response": msg["assistant_response"],
94
- "timestamp": msg["timestamp"].isoformat()
95
- }
96
- for msg in chat["messages"]
97
- ]
98
-
99
- return history
 
 
 
 
100
  except Exception as e:
101
- raise HTTPException(status_code=500, detail="Error retrieving chat history.")
102
 
103
 
104
  @router.post("/new")
@@ -107,10 +113,9 @@ async def start_new_chat(current_user: dict = Depends(get_current_user)):
107
  Start a new chat session by clearing context.
108
  """
109
  try:
110
- # Create a new session entry without history
111
  session_id = ObjectId()
112
  db.chat_histories.insert_one({
113
- "user_id": ObjectId(current_user["_id"]),
114
  "session_id": session_id,
115
  "messages": [],
116
  "created_at": datetime.now(timezone.utc),
@@ -119,4 +124,4 @@ async def start_new_chat(current_user: dict = Depends(get_current_user)):
119
 
120
  return {"message": "New chat started. Previous context cleared.", "session_id": str(session_id)}
121
  except Exception as e:
122
- raise HTTPException(status_code=500, detail="Error starting new chat.")
 
18
  Process user messages using Llama-3.2 and return a response.
19
  """
20
  try:
21
+ user_id = ObjectId(current_user["user_id"])
 
22
 
23
  # Retrieve the latest chat session for context
24
  chat_session = db.chat_histories.find_one({"user_id": user_id}, sort=[("updated_at", -1)])
25
+ session_id = chat_session["session_id"] if chat_session else ObjectId()
26
 
27
  # Process the message with Llama-3.2
28
+ response = process_message(message, chat_session["messages"] if chat_session else [])
29
 
30
  # Save the chat to history
31
  chat_entry = {
 
 
32
  "user_message": message,
33
  "assistant_response": response,
34
  "timestamp": datetime.now(timezone.utc)
35
  }
36
+
37
  db.chat_histories.update_one(
38
  {"user_id": user_id, "session_id": session_id},
39
  {"$push": {"messages": chat_entry}, "$set": {"updated_at": datetime.now(timezone.utc)}},
 
42
 
43
  return {"response": response}
44
  except Exception as e:
45
+ raise HTTPException(status_code=500, detail=f"Error processing message: {str(e)}")
46
 
47
 
48
+ # routes/chat.py
49
+
50
  @router.post("/upload")
51
  async def upload_image(file: UploadFile = File(...), current_user: dict = Depends(get_current_user)):
52
  """
53
  Upload an image for disease detection and return the analysis result.
54
  """
55
  try:
56
+ user_id = ObjectId(current_user["user_id"])
57
+
58
  # Read and analyze the image
59
  file_bytes = await file.read()
60
  analysis_result = detect_disease(file_bytes)
61
 
62
+ if analysis_result.get("name") == "Unknown Image":
63
  raise HTTPException(status_code=400, detail="Invalid image format.")
64
 
65
  # Save the analysis result to the database
66
  analysis_entry = {
67
+ "user_id": user_id,
68
  "file_name": file.filename,
69
  "analysis_result": analysis_result,
70
  "timestamp": datetime.now(timezone.utc)
 
73
 
74
  return {"analysis_result": analysis_result}
75
  except Exception as e:
76
+ raise HTTPException(status_code=500, detail=f"Error analyzing image: {str(e)}")
 
77
 
78
  @router.get("/history")
79
  async def get_chat_history(current_user: dict = Depends(get_current_user)):
 
81
  Retrieve previous chat history for the logged-in user.
82
  """
83
  try:
84
+ user_id = ObjectId(current_user["user_id"])
85
  history = list(db.chat_histories.find({"user_id": user_id}).sort("updated_at", -1))
86
 
87
  # Convert ObjectId to string for JSON serialization
88
+ formatted_history = []
89
  for chat in history:
90
+ formatted_chat = {
91
+ "_id": str(chat["_id"]),
92
+ "session_id": str(chat["session_id"]),
93
+ "updated_at": chat["updated_at"].isoformat(),
94
+ "messages": [
95
+ {
96
+ "user_message": msg["user_message"],
97
+ "assistant_response": msg["assistant_response"],
98
+ "timestamp": msg["timestamp"].isoformat()
99
+ }
100
+ for msg in chat["messages"]
101
+ ]
102
+ }
103
+ formatted_history.append(formatted_chat)
104
+
105
+ return formatted_history
106
  except Exception as e:
107
+ raise HTTPException(status_code=500, detail=f"Error retrieving chat history: {str(e)}")
108
 
109
 
110
  @router.post("/new")
 
113
  Start a new chat session by clearing context.
114
  """
115
  try:
 
116
  session_id = ObjectId()
117
  db.chat_histories.insert_one({
118
+ "user_id": ObjectId(current_user["user_id"]),
119
  "session_id": session_id,
120
  "messages": [],
121
  "created_at": datetime.now(timezone.utc),
 
124
 
125
  return {"message": "New chat started. Previous context cleared.", "session_id": str(session_id)}
126
  except Exception as e:
127
+ raise HTTPException(status_code=500, detail=f"Error starting new chat: {str(e)}")
backend/app/routes/dashboard.py CHANGED
@@ -1,16 +1,64 @@
1
- # backend/app/routes/dashboard.py
2
-
3
- from datetime import datetime
4
  import logging
 
5
  from fastapi import APIRouter, Depends, HTTPException
6
  from backend.app.config.database import get_database
7
  from backend.app.middleware.auth_middleware import get_current_user
 
8
 
9
  # Initialize router and logger
10
  router = APIRouter()
11
  db = get_database()
12
  logger = logging.getLogger(__name__)
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  @router.get("/data")
16
  async def get_dashboard_data(current_user: dict = Depends(get_current_user)):
@@ -27,7 +75,11 @@ async def get_dashboard_data(current_user: dict = Depends(get_current_user)):
27
 
28
  # Fetch user details
29
  user_role = current_user.get("role")
30
- user_farm_id = current_user.get("farm_id")
 
 
 
 
31
 
32
  # Initialize response data
33
  response_data = {
@@ -38,60 +90,33 @@ async def get_dashboard_data(current_user: dict = Depends(get_current_user)):
38
  "recentHealthRecords": []
39
  }
40
 
41
- # Fetch data based on user role
42
  if user_role == "admin":
43
- response_data["tasksCount"] = db.tasks.count_documents({"created_by": current_user["user_id"]})
44
  response_data["inventoryCount"] = db.inventory.count_documents({"farm_id": user_farm_id})
45
  response_data["healthRecordsCount"] = db.health_records.count_documents({"farm_id": user_farm_id})
46
 
47
- # Recent tasks and health records (admin)
48
- response_data["recentTasks"] = list(
49
- db.tasks.find({"created_by": current_user["user_id"]})
50
- .sort("created_at", -1)
51
- .limit(5)
52
- )
53
- response_data["recentHealthRecords"] = list(
54
- db.health_records.find({"farm_id": user_farm_id})
55
- .sort("diagnosis_date", -1)
56
- .limit(5)
57
- )
58
 
59
  elif user_role == "farmer":
60
- response_data["tasksCount"] = db.tasks.count_documents({"assigned_to": current_user["user_id"]})
61
  response_data["healthRecordsCount"] = db.health_records.count_documents({"farm_id": user_farm_id})
62
 
63
- # Recent tasks and health records (farmer)
64
- response_data["recentTasks"] = list(
65
- db.tasks.find({"assigned_to": current_user["user_id"]})
66
- .sort("created_at", -1)
67
- .limit(5)
68
- )
69
- response_data["recentHealthRecords"] = list(
70
- db.health_records.find({"farm_id": user_farm_id})
71
- .sort("diagnosis_date", -1)
72
- .limit(5)
73
- )
74
-
75
- # Convert MongoDB ObjectId to strings and ensure datetime fields are serialized correctly
76
- for task in response_data["recentTasks"]:
77
- task["_id"] = str(task["_id"])
78
- # Safely handle due_date
79
- if isinstance(task.get("due_date"), datetime):
80
- task["due_date"] = task["due_date"].isoformat()
81
- else:
82
- task["due_date"] = task.get("due_date", "N/A")
83
-
84
- for record in response_data["recentHealthRecords"]:
85
- record["_id"] = str(record["_id"])
86
- # Safely handle diagnosis_date
87
- if isinstance(record.get("diagnosis_date"), datetime):
88
- record["diagnosis_date"] = record["diagnosis_date"].isoformat()
89
- else:
90
- record["diagnosis_date"] = record.get("diagnosis_date", "N/A")
91
 
92
  logger.info("Dashboard data fetched successfully for user: %s", current_user["email"])
93
  return response_data
94
 
95
  except Exception as e:
96
- logger.error("Error fetching dashboard data: %s", str(e))
97
  raise HTTPException(status_code=500, detail="Internal server error")
 
1
+ from datetime import datetime, timezone
 
 
2
  import logging
3
+ from bson import ObjectId
4
  from fastapi import APIRouter, Depends, HTTPException
5
  from backend.app.config.database import get_database
6
  from backend.app.middleware.auth_middleware import get_current_user
7
+ from backend.app.schemas.task_schema import TaskSchema
8
 
9
  # Initialize router and logger
10
  router = APIRouter()
11
  db = get_database()
12
  logger = logging.getLogger(__name__)
13
 
14
+ def parse_datetime(date):
15
+ """
16
+ Parse a date field into ISO format. If it's already a string, return it.
17
+ If it's None, return a default value.
18
+ """
19
+ if isinstance(date, str):
20
+ try:
21
+ # Attempt to parse the string as a datetime
22
+ parsed_date = datetime.fromisoformat(date)
23
+ return parsed_date.astimezone(timezone.utc).isoformat()
24
+ except ValueError:
25
+ return date # Return the original string if parsing fails
26
+ elif isinstance(date, datetime):
27
+ return date.astimezone(timezone.utc).isoformat()
28
+ else:
29
+ return datetime.now(timezone.utc).isoformat() # Default to now if no valid date
30
+
31
+
32
+ def format_health_records(records_cursor):
33
+ """
34
+ Format health records for response.
35
+ """
36
+ return [
37
+ {
38
+ "_id": str(record["_id"]),
39
+ "disease_type": record.get("disease_type", "N/A"),
40
+ "diagnosis_date": parse_datetime(record.get("diagnosis_date"))
41
+ }
42
+ for record in records_cursor
43
+ ]
44
+
45
+
46
+ def format_tasks(tasks_cursor):
47
+ """
48
+ Format tasks for response using TaskSchema.
49
+ """
50
+ return [
51
+ TaskSchema(
52
+ id=task["_id"],
53
+ task_description=task.get("task_description", "N/A"),
54
+ assigned_to=task.get("assigned_to"),
55
+ status=task.get("status", "pending"),
56
+ created_at=task.get("created_at"),
57
+ due_date=task.get("due_date")
58
+ ).model_dump(by_alias=True)
59
+ for task in tasks_cursor
60
+ ]
61
+
62
 
63
  @router.get("/data")
64
  async def get_dashboard_data(current_user: dict = Depends(get_current_user)):
 
75
 
76
  # Fetch user details
77
  user_role = current_user.get("role")
78
+ user_farm_id = ObjectId(current_user["farm_id"])
79
+ user_id = ObjectId(current_user["user_id"])
80
+ logger.debug("User ID: %s", user_id)
81
+ logger.debug("User Role: %s", user_role)
82
+ logger.debug("Farm ID: %s", user_farm_id)
83
 
84
  # Initialize response data
85
  response_data = {
 
90
  "recentHealthRecords": []
91
  }
92
 
93
+ # Fetch counts and recent data based on user role
94
  if user_role == "admin":
95
+ response_data["tasksCount"] = db.tasks.count_documents({})
96
  response_data["inventoryCount"] = db.inventory.count_documents({"farm_id": user_farm_id})
97
  response_data["healthRecordsCount"] = db.health_records.count_documents({"farm_id": user_farm_id})
98
 
99
+ # Fetch and format recent tasks and health records
100
+ recent_tasks_cursor = db.tasks.find({}).sort("created_at", -1).limit(5)
101
+ response_data["recentTasks"] = format_tasks(recent_tasks_cursor)
102
+
103
+ recent_health_records_cursor = db.health_records.find({"farm_id": user_farm_id}).sort("diagnosis_date", -1).limit(5)
104
+ response_data["recentHealthRecords"] = format_health_records(recent_health_records_cursor)
 
 
 
 
 
105
 
106
  elif user_role == "farmer":
107
+ response_data["tasksCount"] = db.tasks.count_documents({"assigned_to": user_id})
108
  response_data["healthRecordsCount"] = db.health_records.count_documents({"farm_id": user_farm_id})
109
 
110
+ # Fetch and format recent tasks and health records
111
+ recent_tasks_cursor = db.tasks.find({"assigned_to": user_id}).sort("created_at", -1).limit(5)
112
+ response_data["recentTasks"] = format_tasks(recent_tasks_cursor)
113
+
114
+ recent_health_records_cursor = db.health_records.find({"farm_id": user_farm_id}).sort("diagnosis_date", -1).limit(5)
115
+ response_data["recentHealthRecords"] = format_health_records(recent_health_records_cursor)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
  logger.info("Dashboard data fetched successfully for user: %s", current_user["email"])
118
  return response_data
119
 
120
  except Exception as e:
121
+ logger.error("Error fetching dashboard data: %s", str(e), exc_info=True)
122
  raise HTTPException(status_code=500, detail="Internal server error")
backend/app/static/css/chat.css ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* General Reset */
2
+
3
+ body {
4
+ margin: 0;
5
+ font-family: Arial, sans-serif;
6
+ background-color: #f7f8fc;
7
+ display: flex;
8
+ height: 100vh;
9
+ overflow: hidden;
10
+ }
11
+
12
+ .chatgpt-clone {
13
+ display: flex;
14
+ width: 100%;
15
+ height: 100%;
16
+ }
17
+
18
+ /* Sidebar Styling */
19
+ .chat-sidebar {
20
+ width: 25%;
21
+ background-color: #2c2c54;
22
+ color: white;
23
+ display: flex;
24
+ flex-direction: column;
25
+ padding: 20px;
26
+ overflow-y: auto;
27
+ }
28
+
29
+ .chat-sidebar h2 {
30
+ margin-top: 0;
31
+ font-size: 20px;
32
+ border-bottom: 1px solid #4e4e8f;
33
+ padding-bottom: 10px;
34
+ }
35
+
36
+ .chat-sidebar ul {
37
+ list-style-type: none;
38
+ padding: 0;
39
+ margin: 0;
40
+ flex: 1;
41
+ overflow-y: auto;
42
+ }
43
+
44
+ .chat-sidebar ul li {
45
+ padding: 10px;
46
+ margin: 5px 0;
47
+ background-color: #393975;
48
+ border-radius: 5px;
49
+ cursor: pointer;
50
+ transition: background-color 0.3s;
51
+ }
52
+
53
+ .chat-sidebar ul li:hover {
54
+ background-color: #5050a5;
55
+ }
56
+
57
+ .new-chat-button {
58
+ padding: 10px;
59
+ background-color: #5050a5;
60
+ border: none;
61
+ color: white;
62
+ border-radius: 5px;
63
+ cursor: pointer;
64
+ font-size: 16px;
65
+ margin-top: 10px;
66
+ }
67
+
68
+ .new-chat-button:hover {
69
+ background-color: #7070c0;
70
+ }
71
+
72
+ /* Main Chat Interface */
73
+ .chat-main {
74
+ width: 75%;
75
+ display: flex;
76
+ flex-direction: column;
77
+ background-color: #ffffff;
78
+ }
79
+
80
+ .chat-header {
81
+ background-color: #f7f8fc;
82
+ padding: 20px;
83
+ border-bottom: 1px solid #e2e2e2;
84
+ }
85
+
86
+ .chat-header h1 {
87
+ margin: 0;
88
+ font-size: 22px;
89
+ color: #2c2c54;
90
+ }
91
+
92
+ .chat-window {
93
+ flex: 1;
94
+ overflow-y: auto;
95
+ padding: 20px;
96
+ display: flex;
97
+ flex-direction: column;
98
+ gap: 15px;
99
+ background-color: #f5f5f5;
100
+ }
101
+
102
+ .chat-bubble {
103
+ max-width: 70%;
104
+ padding: 15px;
105
+ border-radius: 15px;
106
+ word-wrap: break-word;
107
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
108
+ }
109
+
110
+ .user-message {
111
+ align-self: flex-end;
112
+ background-color: #3b82f6;
113
+ color: white;
114
+ border-bottom-right-radius: 0;
115
+ }
116
+
117
+ .bot-response {
118
+ align-self: flex-start;
119
+ background-color: #e2e8f0;
120
+ color: #2c2c54;
121
+ border-bottom-left-radius: 0;
122
+ }
123
+
124
+ .chat-footer {
125
+ padding: 10px 20px;
126
+ background-color: #f7f8fc;
127
+ display: flex;
128
+ align-items: center;
129
+ border-top: 1px solid #e2e2e2;
130
+ }
131
+
132
+ textarea#messageInput {
133
+ flex: 1;
134
+ resize: none;
135
+ border: 1px solid #d1d5db;
136
+ border-radius: 5px;
137
+ padding: 10px;
138
+ font-size: 16px;
139
+ }
140
+
141
+ .send-button {
142
+ background-color: #3b82f6;
143
+ color: white;
144
+ border: none;
145
+ padding: 10px 15px;
146
+ margin-left: 10px;
147
+ border-radius: 5px;
148
+ cursor: pointer;
149
+ }
150
+
151
+ .send-button i {
152
+ font-size: 18px;
153
+ }
154
+
155
+ .send-button:hover {
156
+ background-color: #2563eb;
157
+ }
158
+
159
+ /* chat.css */
160
+
161
+ /* ... Existing styles ... */
162
+
163
+ /* Attachment Icon */
164
+ .attachment-icon {
165
+ color: #3b82f6;
166
+ font-size: 20px;
167
+ margin-right: 10px;
168
+ cursor: pointer;
169
+ align-self: center;
170
+ }
171
+
172
+ .attachment-icon:hover {
173
+ color: #2563eb;
174
+ }
175
+
176
+ /* Loading Indicator */
177
+ .loading-indicator {
178
+ align-self: center;
179
+ font-size: 24px;
180
+ color: #999;
181
+ display: flex;
182
+ justify-content: center;
183
+ align-items: center;
184
+ }
185
+
186
+ .loading-indicator img {
187
+ width: 40px;
188
+ height: 40px;
189
+ }
190
+
191
+ /* Chat Image */
192
+ .chat-image {
193
+ max-width: 100%;
194
+ height: auto;
195
+ border-radius: 5px;
196
+ }
197
+ /* chat.css */
198
+
199
+ /* Chat Interface Layout */
200
+ .chat-interface {
201
+ display: flex;
202
+ height: 100vh;
203
+ overflow: hidden;
204
+ }
205
+
206
+ .chat-sidebar {
207
+ width: 25%;
208
+ background: #f4f4f4;
209
+ border-right: 1px solid #ddd;
210
+ overflow-y: auto;
211
+ padding: 15px;
212
+ }
213
+
214
+ .chat-sidebar h2 {
215
+ margin-bottom: 15px;
216
+ }
217
+
218
+ .chat-sidebar ul {
219
+ list-style: none;
220
+ padding: 0;
221
+ }
222
+
223
+ .chat-sidebar ul li {
224
+ padding: 10px;
225
+ background: #fff;
226
+ border: 1px solid #ddd;
227
+ margin-bottom: 10px;
228
+ cursor: pointer;
229
+ border-radius: 5px;
230
+ }
231
+
232
+ .chat-sidebar ul li:hover {
233
+ background: #e9ecef;
234
+ }
235
+
236
+ /* Chat Main Section */
237
+ .chat-main {
238
+ flex: 1;
239
+ display: flex;
240
+ flex-direction: column;
241
+ }
242
+
243
+ .chat-header {
244
+ background: #007bff;
245
+ color: #fff;
246
+ padding: 10px;
247
+ text-align: center;
248
+ font-size: 18px;
249
+ }
250
+
251
+ .chat-window {
252
+ flex: 1;
253
+ overflow-y: auto;
254
+ padding: 15px;
255
+ display: flex;
256
+ flex-direction: column-reverse;
257
+ background: #f9f9f9;
258
+ }
259
+
260
+ /* Chat Footer */
261
+ .chat-footer {
262
+ display: flex;
263
+ align-items: center;
264
+ padding: 10px;
265
+ background: #f1f1f1;
266
+ border-top: 1px solid #ddd;
267
+ }
268
+
269
+ .chat-footer textarea {
270
+ flex: 1;
271
+ height: 40px;
272
+ padding: 10px;
273
+ border: 1px solid #ddd;
274
+ border-radius: 5px;
275
+ margin-right: 10px;
276
+ }
277
+
278
+ .chat-footer .send-button {
279
+ background: #007bff;
280
+ color: #fff;
281
+ border: none;
282
+ padding: 10px 15px;
283
+ border-radius: 5px;
284
+ cursor: pointer;
285
+ }
286
+
287
+ .chat-footer .send-button:hover {
288
+ background: #0056b3;
289
+ }
290
+
291
+ .chat-footer .attachment-icon {
292
+ font-size: 18px;
293
+ margin-right: 10px;
294
+ cursor: pointer;
295
+ }
296
+
297
+ /* Chat Bubbles */
298
+ .chat-bubble {
299
+ max-width: 70%;
300
+ margin: 10px 0;
301
+ padding: 10px 15px;
302
+ border-radius: 10px;
303
+ position: relative;
304
+ }
305
+
306
+ .user-message {
307
+ background: #007bff;
308
+ color: #fff;
309
+ align-self: flex-end;
310
+ border-bottom-right-radius: 0;
311
+ }
312
+
313
+ .bot-response {
314
+ background: #e9ecef;
315
+ align-self: flex-start;
316
+ border-bottom-left-radius: 0;
317
+ }
318
+
319
+ /* Chat Image */
320
+ .chat-image {
321
+ max-width: 100%;
322
+ border-radius: 5px;
323
+ }
324
+
backend/app/static/js/chat.js ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // chat.js
2
+
3
+ // Fetch chat history and populate the sidebar
4
+ async function fetchChatHistory() {
5
+ try {
6
+ const response = await fetch("/chat/history", {
7
+ method: "GET",
8
+ headers: { "Authorization": "Bearer " + sessionStorage.getItem("access_token") }
9
+ });
10
+
11
+ if (!response.ok) throw new Error("Failed to fetch chat history.");
12
+
13
+ const history = await response.json();
14
+ const historyList = document.getElementById("chatHistoryList");
15
+ historyList.innerHTML = "";
16
+
17
+ history.forEach(session => {
18
+ const listItem = document.createElement("li");
19
+ listItem.textContent = `Chat started at ${new Date(session.created_at).toLocaleString()}`;
20
+ listItem.dataset.sessionId = session.session_id;
21
+ listItem.onclick = () => loadChatSession(session.session_id);
22
+ historyList.appendChild(listItem);
23
+ });
24
+ } catch (error) {
25
+ console.error("Error fetching chat history:", error);
26
+ }
27
+ }
28
+
29
+ // Load a specific chat session
30
+ async function loadChatSession(sessionId) {
31
+ try {
32
+ const response = await fetch(`/chat/history/${sessionId}`, {
33
+ method: "GET",
34
+ headers: { "Authorization": "Bearer " + sessionStorage.getItem("access_token") }
35
+ });
36
+
37
+ if (!response.ok) throw new Error("Failed to load chat session.");
38
+
39
+ const session = await response.json();
40
+ const chatWindow = document.getElementById("chatWindow");
41
+ chatWindow.innerHTML = "";
42
+
43
+ session.messages.forEach(msg => {
44
+ addMessageToChatWindow(msg.user_message, "user-message");
45
+ addMessageToChatWindow(msg.assistant_response, "bot-response");
46
+ });
47
+ } catch (error) {
48
+ console.error("Error loading chat session:", error);
49
+ }
50
+ }
51
+
52
+ // Send a new message
53
+ async function sendMessage(event) {
54
+ event.preventDefault();
55
+ const messageInput = document.getElementById("messageInput");
56
+ const message = messageInput.value.trim();
57
+ if (!message) return;
58
+
59
+ addMessageToChatWindow(message, "user-message");
60
+ messageInput.value = "";
61
+
62
+ const loadingIndicator = addLoadingIndicator();
63
+
64
+ try {
65
+ const response = await fetch("/chat", {
66
+ method: "POST",
67
+ headers: { "Content-Type": "application/json", "Authorization": "Bearer " + sessionStorage.getItem("access_token") },
68
+ body: JSON.stringify({ message })
69
+ });
70
+
71
+ if (!response.ok) throw new Error("Failed to fetch AI response.");
72
+
73
+ const data = await response.json();
74
+ removeLoadingIndicator(loadingIndicator);
75
+ addMessageToChatWindow(data.response, "bot-response");
76
+ } catch (error) {
77
+ console.error("Error sending message:", error);
78
+ removeLoadingIndicator(loadingIndicator);
79
+ addMessageToChatWindow("An error occurred while processing your message.", "bot-response");
80
+ }
81
+ }
82
+
83
+ // Add message to chat window
84
+ function addMessageToChatWindow(content, className) {
85
+ const chatWindow = document.getElementById("chatWindow");
86
+ const bubble = document.createElement("div");
87
+ bubble.className = `chat-bubble ${className}`;
88
+ bubble.textContent = content;
89
+ chatWindow.prepend(bubble);
90
+ }
91
+
92
+ // Add loading indicator
93
+ function addLoadingIndicator() {
94
+ const chatWindow = document.getElementById("chatWindow");
95
+ const indicator = document.createElement("div");
96
+ indicator.className = "loading-indicator";
97
+ indicator.textContent = "Typing...";
98
+ chatWindow.prepend(indicator);
99
+ return indicator;
100
+ }
101
+
102
+ // Remove loading indicator
103
+ function removeLoadingIndicator(indicator) {
104
+ indicator.remove();
105
+ }
106
+
107
+ // Initialize chat on page load
108
+ document.addEventListener("DOMContentLoaded", () => {
109
+ fetchChatHistory();
110
+ });
backend/app/static/js/dashboard.js CHANGED
@@ -1,20 +1,47 @@
1
  async function loadDashboardData() {
 
 
 
2
  const token = sessionStorage.getItem('access_token');
3
  if (!token) {
4
- window.location.href = "/login";
5
  return;
6
  }
7
 
8
  try {
9
  const response = await fetch('/dashboard/data', {
10
- headers: {'Authorization': 'Bearer ' + token}
11
  });
12
 
13
  if (response.ok) {
14
  const data = await response.json();
15
- document.getElementById('recentTasks').innerHTML = data.recentTasks.map(task => `<li>${task.description}</li>`).join('');
16
- document.getElementById('inventoryCount').innerText = data.inventoryCount;
17
- document.getElementById('healthRecordsCount').innerText = data.healthRecordsCount;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  } else {
19
  console.error("Error fetching dashboard data:", response.statusText);
20
  }
@@ -23,4 +50,5 @@ async function loadDashboardData() {
23
  }
24
  }
25
 
 
26
  document.addEventListener("DOMContentLoaded", loadDashboardData);
 
1
  async function loadDashboardData() {
2
+ /**
3
+ * Fetch and load dashboard data including tasks, inventory count, and health records.
4
+ */
5
  const token = sessionStorage.getItem('access_token');
6
  if (!token) {
7
+ window.location.href = "/login"; // Redirect to login if token is missing
8
  return;
9
  }
10
 
11
  try {
12
  const response = await fetch('/dashboard/data', {
13
+ headers: { 'Authorization': 'Bearer ' + token }
14
  });
15
 
16
  if (response.ok) {
17
  const data = await response.json();
18
+
19
+ // Update Statistics Section
20
+ document.getElementById('tasksCount').innerText = data.tasksCount || 0;
21
+ document.getElementById('inventoryCount').innerText = data.inventoryCount || 0;
22
+ document.getElementById('healthRecordsCount').innerText = data.healthRecordsCount || 0;
23
+
24
+ // Update Recent Tasks Section
25
+ const recentTasks = data.recentTasks || [];
26
+ document.getElementById('recentTasks').innerHTML = recentTasks.length
27
+ ? recentTasks.map(task => `
28
+ <li class="list-group-item">
29
+ ${task.description}
30
+ (Due: ${new Date(task.due_date).toLocaleDateString()})
31
+ </li>
32
+ `).join('')
33
+ : '<li class="list-group-item">No recent tasks available.</li>';
34
+
35
+ // Update Recent Health Records Section
36
+ const recentHealthRecords = data.recentHealthRecords || [];
37
+ document.getElementById('recentHealthRecords').innerHTML = recentHealthRecords.length
38
+ ? recentHealthRecords.map(record => `
39
+ <li class="list-group-item">
40
+ ${record.disease_type}
41
+ (Logged: ${new Date(record.diagnosis_date).toLocaleDateString()})
42
+ </li>
43
+ `).join('')
44
+ : '<li class="list-group-item">No recent health records available.</li>';
45
  } else {
46
  console.error("Error fetching dashboard data:", response.statusText);
47
  }
 
50
  }
51
  }
52
 
53
+ // Load dashboard data when the DOM is fully loaded
54
  document.addEventListener("DOMContentLoaded", loadDashboardData);
backend/app/templates/chat/index.html CHANGED
@@ -3,279 +3,47 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>AI Chat</title>
7
-
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
9
- <style>
10
- /* General Reset */
11
-
12
- body {
13
- margin: 0;
14
- font-family: Arial, sans-serif;
15
- background-color: #f7f8fc;
16
- display: flex;
17
- height: 100vh;
18
- overflow: hidden;
19
- }
20
-
21
- .chatgpt-clone {
22
- display: flex;
23
- width: 100%;
24
- height: 100%;
25
- }
26
-
27
- /* Sidebar Styling */
28
- .chat-sidebar {
29
- width: 25%;
30
- background-color: #2c2c54;
31
- color: white;
32
- display: flex;
33
- flex-direction: column;
34
- padding: 20px;
35
- overflow-y: auto;
36
- }
37
-
38
- .chat-sidebar h2 {
39
- margin-top: 0;
40
- font-size: 20px;
41
- border-bottom: 1px solid #4e4e8f;
42
- padding-bottom: 10px;
43
- }
44
-
45
- .chat-sidebar ul {
46
- list-style-type: none;
47
- padding: 0;
48
- margin: 0;
49
- flex: 1;
50
- overflow-y: auto;
51
- }
52
-
53
- .chat-sidebar ul li {
54
- padding: 10px;
55
- margin: 5px 0;
56
- background-color: #393975;
57
- border-radius: 5px;
58
- cursor: pointer;
59
- transition: background-color 0.3s;
60
- }
61
-
62
- .chat-sidebar ul li:hover {
63
- background-color: #5050a5;
64
- }
65
-
66
- .new-chat-button {
67
- padding: 10px;
68
- background-color: #5050a5;
69
- border: none;
70
- color: white;
71
- border-radius: 5px;
72
- cursor: pointer;
73
- font-size: 16px;
74
- margin-top: 10px;
75
- }
76
-
77
- .new-chat-button:hover {
78
- background-color: #7070c0;
79
- }
80
-
81
- /* Main Chat Interface */
82
- .chat-main {
83
- width: 75%;
84
- display: flex;
85
- flex-direction: column;
86
- background-color: #ffffff;
87
- }
88
-
89
- .chat-header {
90
- background-color: #f7f8fc;
91
- padding: 20px;
92
- border-bottom: 1px solid #e2e2e2;
93
- }
94
-
95
- .chat-header h1 {
96
- margin: 0;
97
- font-size: 22px;
98
- color: #2c2c54;
99
- }
100
-
101
- .chat-window {
102
- flex: 1;
103
- overflow-y: auto;
104
- padding: 20px;
105
- display: flex;
106
- flex-direction: column;
107
- gap: 15px;
108
- background-color: #f5f5f5;
109
- }
110
-
111
- .chat-bubble {
112
- max-width: 70%;
113
- padding: 15px;
114
- border-radius: 15px;
115
- word-wrap: break-word;
116
- box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
117
- }
118
-
119
- .user-message {
120
- align-self: flex-end;
121
- background-color: #3b82f6;
122
- color: white;
123
- border-bottom-right-radius: 0;
124
- }
125
-
126
- .bot-response {
127
- align-self: flex-start;
128
- background-color: #e2e8f0;
129
- color: #2c2c54;
130
- border-bottom-left-radius: 0;
131
- }
132
-
133
- .chat-footer {
134
- padding: 10px 20px;
135
- background-color: #f7f8fc;
136
- display: flex;
137
- align-items: center;
138
- border-top: 1px solid #e2e2e2;
139
- }
140
-
141
- textarea#messageInput {
142
- flex: 1;
143
- resize: none;
144
- border: 1px solid #d1d5db;
145
- border-radius: 5px;
146
- padding: 10px;
147
- font-size: 16px;
148
- }
149
-
150
- .send-button {
151
- background-color: #3b82f6;
152
- color: white;
153
- border: none;
154
- padding: 10px 15px;
155
- margin-left: 10px;
156
- border-radius: 5px;
157
- cursor: pointer;
158
- }
159
-
160
- .send-button i {
161
- font-size: 18px;
162
- }
163
-
164
- .send-button:hover {
165
- background-color: #2563eb;
166
- }
167
- </style>
168
  </head>
169
  <body>
170
- <div class="chatgpt-clone">
171
- <!-- Sidebar for Chat History -->
172
- <aside class="chat-sidebar">
173
- <h2>Chat History</h2>
174
- <ul id="chatHistoryList">
175
- <!-- Chat history dynamically loaded here -->
176
- </ul>
177
- <button class="new-chat-button" onclick="startNewChat()">+ New Chat</button>
178
- </aside>
179
-
180
- <!-- Main Chat Interface -->
181
- <main class="chat-main">
182
- <header class="chat-header">
183
- <h1>AI Assistant</h1>
184
- </header>
185
-
186
- <div class="chat-window" id="chatWindow">
187
- <!-- Chat messages dynamically loaded here -->
188
- </div>
189
-
190
- <footer class="chat-footer">
 
 
 
191
  <textarea
192
- id="messageInput"
193
- placeholder="Type your message..."
194
- onkeydown="if (event.key === 'Enter' && !event.shiftKey) sendMessage(event);"
195
  ></textarea>
196
- <button class="send-button" onclick="sendMessage(event)">
197
- <i class="fas fa-paper-plane"></i>
198
- </button>
199
- </footer>
200
- </main>
201
- </div>
202
- <script>
203
- // Fetch chat history and populate the sidebar
204
- async function fetchChatHistory() {
205
- try {
206
- const response = await fetch("/chat/history");
207
- if (!response.ok) {
208
- throw new Error("Failed to fetch chat history.");
209
- }
210
-
211
- const history = await response.json();
212
- const historyList = document.getElementById("chatHistoryList");
213
- historyList.innerHTML = "";
214
-
215
- history.forEach(chat => {
216
- const listItem = document.createElement("li");
217
- listItem.textContent = chat.message;
218
- listItem.onclick = () => loadChat(chat);
219
- historyList.appendChild(listItem);
220
- });
221
- } catch (error) {
222
- console.error("Error fetching chat history:", error);
223
- }
224
- }
225
-
226
- // Send a new message
227
- async function sendMessage(event) {
228
- event.preventDefault();
229
- const messageInput = document.getElementById("messageInput");
230
- const message = messageInput.value.trim();
231
-
232
- if (!message) return;
233
-
234
- // Add user message to the chat window
235
- addMessageToChatWindow(message, "user-message");
236
-
237
- try {
238
- const response = await fetch("/chat", {
239
- method: "POST",
240
- headers: {"Content-Type": "application/json"},
241
- body: JSON.stringify({message}),
242
- });
243
-
244
- const data = await response.json();
245
- addMessageToChatWindow(data.response, "bot-response");
246
- } catch (error) {
247
- console.error("Error sending message:", error);
248
- }
249
-
250
- messageInput.value = "";
251
- }
252
-
253
- // Add a message to the chat window
254
- function addMessageToChatWindow(message, className) {
255
- const chatWindow = document.getElementById("chatWindow");
256
- const bubble = document.createElement("div");
257
- bubble.className = `chat-bubble ${className}`;
258
- bubble.textContent = message;
259
- chatWindow.appendChild(bubble);
260
- chatWindow.scrollTop = chatWindow.scrollHeight;
261
- }
262
-
263
- // Start a new chat session
264
- async function startNewChat() {
265
- try {
266
- await fetch("/chat/new", {method: "POST"});
267
- const chatWindow = document.getElementById("chatWindow");
268
- chatWindow.innerHTML = "";
269
- } catch (error) {
270
- console.error("Error starting new chat:", error);
271
- }
272
- }
273
-
274
- // Initialize
275
- document.addEventListener("DOMContentLoaded", () => {
276
- fetchChatHistory();
277
- });
278
-
279
- </script>
280
  </body>
281
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ChatGPT Clone</title>
7
+ <link rel="stylesheet" href="/static/css/chat.css">
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  </head>
10
  <body>
11
+ <div class="chat-interface">
12
+ <!-- Sidebar for Chat History -->
13
+ <aside class="chat-sidebar">
14
+ <h2>Chat History</h2>
15
+ <ul id="chatHistoryList">
16
+ <!-- Chat history dynamically loaded here -->
17
+ </ul>
18
+ </aside>
19
+
20
+ <!-- Main Chat Section -->
21
+ <main class="chat-main">
22
+ <header class="chat-header">
23
+ <h1>AI Assistant</h1>
24
+ </header>
25
+
26
+ <div class="chat-window" id="chatWindow">
27
+ <!-- Chat bubbles dynamically added here -->
28
+ </div>
29
+
30
+ <footer class="chat-footer">
31
+ <label for="fileInput" class="attachment-icon">
32
+ <i class="fas fa-paperclip"></i>
33
+ </label>
34
+ <input type="file" id="fileInput" accept="image/*" style="display: none;" onchange="uploadImage(event)">
35
  <textarea
36
+ id="messageInput"
37
+ placeholder="Type your message..."
38
+ onkeydown="if (event.key === 'Enter' && !event.shiftKey) sendMessage(event);"
39
  ></textarea>
40
+ <button class="send-button" onclick="sendMessage(event)">
41
+ <i class="fas fa-paper-plane"></i>
42
+ </button>
43
+ </footer>
44
+ </main>
45
+ </div>
46
+
47
+ <script src="/static/js/chat.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  </body>
49
  </html>
requirements.txt CHANGED
@@ -3,7 +3,7 @@ opencv-python-headless
3
  fastapi~=0.112.4
4
  passlib[bcrypt]
5
  pydantic[email]~=2.9.2
6
- pymongo~=4.8.0
7
  python-dotenv~=1.0.1
8
  python-jose[cryptography]
9
  starlette
 
3
  fastapi~=0.112.4
4
  passlib[bcrypt]
5
  pydantic[email]~=2.9.2
6
+ pymongo~=4.10.1
7
  python-dotenv~=1.0.1
8
  python-jose[cryptography]
9
  starlette