Emmanuel Frimpong Asante commited on
Commit
c36974c
·
1 Parent(s): 6dbfc52

update space

Browse files
.idea/workspace.xml CHANGED
@@ -5,13 +5,12 @@
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$/models/schemas/todo_schema.py" afterDir="false" />
9
- <change afterPath="$PROJECT_DIR$/routes/todo.py" afterDir="false" />
10
- <change afterPath="$PROJECT_DIR$/templates/todos.html" afterDir="false" />
11
  <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
12
  <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
13
- <change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
14
- <change beforePath="$PROJECT_DIR$/services/auth_service.py" beforeDir="false" afterPath="$PROJECT_DIR$/services/auth_service.py" afterDir="false" />
15
  </list>
16
  <option name="SHOW_DIALOG" value="false" />
17
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -24,8 +23,8 @@
24
  <option value="Dockerfile" />
25
  <option value="JavaScript File" />
26
  <option value="CSS File" />
27
- <option value="Python Script" />
28
  <option value="HTML File" />
 
29
  </list>
30
  </option>
31
  </component>
@@ -124,15 +123,7 @@
124
  <workItem from="1730338434025" duration="11075000" />
125
  <workItem from="1730305361670" duration="2425000" />
126
  <workItem from="1730378091154" duration="2435000" />
127
- <workItem from="1730381153348" duration="7510000" />
128
- </task>
129
- <task id="LOCAL-00022" summary="update space">
130
- <option name="closed" value="true" />
131
- <created>1730325791987</created>
132
- <option name="number" value="00022" />
133
- <option name="presentableId" value="LOCAL-00022" />
134
- <option name="project" value="LOCAL" />
135
- <updated>1730325791987</updated>
136
  </task>
137
  <task id="LOCAL-00023" summary="update space">
138
  <option name="closed" value="true" />
@@ -518,7 +509,15 @@
518
  <option name="project" value="LOCAL" />
519
  <updated>1730387397425</updated>
520
  </task>
521
- <option name="localTasksCounter" value="71" />
 
 
 
 
 
 
 
 
522
  <servers />
523
  </component>
524
  <component name="TypeScriptGeneratedFilesManager">
 
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$/models/schemas/log_schema.py" afterDir="false" />
9
+ <change afterPath="$PROJECT_DIR$/routes/report_dashboard.py" afterDir="false" />
10
+ <change afterPath="$PROJECT_DIR$/templates/report_dashboard.html" afterDir="false" />
11
  <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
12
  <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
13
+ <change beforePath="$PROJECT_DIR$/services/utils.py" beforeDir="false" afterPath="$PROJECT_DIR$/services/utils.py" afterDir="false" />
 
14
  </list>
15
  <option name="SHOW_DIALOG" value="false" />
16
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
 
23
  <option value="Dockerfile" />
24
  <option value="JavaScript File" />
25
  <option value="CSS File" />
 
26
  <option value="HTML File" />
27
+ <option value="Python Script" />
28
  </list>
29
  </option>
30
  </component>
 
123
  <workItem from="1730338434025" duration="11075000" />
124
  <workItem from="1730305361670" duration="2425000" />
125
  <workItem from="1730378091154" duration="2435000" />
126
+ <workItem from="1730381153348" duration="8786000" />
 
 
 
 
 
 
 
 
127
  </task>
128
  <task id="LOCAL-00023" summary="update space">
129
  <option name="closed" value="true" />
 
509
  <option name="project" value="LOCAL" />
510
  <updated>1730387397425</updated>
511
  </task>
512
+ <task id="LOCAL-00071" summary="update space">
513
+ <option name="closed" value="true" />
514
+ <created>1730389031505</created>
515
+ <option name="number" value="00071" />
516
+ <option name="presentableId" value="LOCAL-00071" />
517
+ <option name="project" value="LOCAL" />
518
+ <updated>1730389031505</updated>
519
+ </task>
520
+ <option name="localTasksCounter" value="72" />
521
  <servers />
522
  </component>
523
  <component name="TypeScriptGeneratedFilesManager">
README.md CHANGED
@@ -69,8 +69,8 @@ This project combines a **Poultry Farming Assistance System** and a **Poultry Ma
69
  ### 4. To-Do List Management
70
  - [x] Design MongoDB schema for to-do lists.
71
  - [x] Create routes for farmers to view and mark to-do items.
72
- - [ ] Build functionality for admins to assign tasks.
73
- - [ ] Develop an **AdminLTE 4**-based UI for managing tasks.
74
 
75
  ### 5. Data Logging and Reporting
76
  - [ ] Implement data logging for activities and health records.
 
69
  ### 4. To-Do List Management
70
  - [x] Design MongoDB schema for to-do lists.
71
  - [x] Create routes for farmers to view and mark to-do items.
72
+ - [x] Build functionality for admins to assign tasks.
73
+ - [x] Develop an **AdminLTE 4**-based UI for managing tasks.
74
 
75
  ### 5. Data Logging and Reporting
76
  - [ ] Implement data logging for activities and health records.
models/schemas/log_schema.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # models/schemas/log_schema.py
2
+
3
+ from pydantic import BaseModel, Field
4
+ from datetime import datetime
5
+ from typing import Optional
6
+ from bson import ObjectId
7
+
8
+ class PyObjectId(ObjectId):
9
+ """Custom ObjectId class for Pydantic validation."""
10
+ @classmethod
11
+ def __get_validators__(cls):
12
+ yield cls.validate
13
+
14
+ @classmethod
15
+ def validate(cls, v):
16
+ if not ObjectId.is_valid(v):
17
+ raise ValueError("Invalid ObjectId")
18
+ return ObjectId(v)
19
+
20
+ class ActivityLog(BaseModel):
21
+ id: PyObjectId = Field(default_factory=PyObjectId, alias="_id")
22
+ activity_type: str # e.g., "health_check", "feeding", "medication"
23
+ description: str
24
+ user_id: str
25
+ timestamp: datetime = Field(default_factory=datetime.utcnow)
26
+
27
+ class Config:
28
+ json_encoders = {ObjectId: str}
requirements.txt CHANGED
@@ -17,3 +17,5 @@ transformers
17
  keras
18
  torch
19
  bcrypt>=3.2.0
 
 
 
17
  keras
18
  torch
19
  bcrypt>=3.2.0
20
+ reportlab
21
+ pandas
routes/report_dashboard.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Request, Depends
2
+ from fastapi.templating import Jinja2Templates
3
+ from services.utils import db, log_activity
4
+ from services.auth_service import get_current_user
5
+ from starlette.responses import HTMLResponse, JSONResponse
6
+ from fastapi.responses import StreamingResponse
7
+ from reportlab.lib.pagesizes import letter
8
+ from reportlab.pdfgen import canvas
9
+ import io
10
+ import pandas as pd
11
+ from fastapi.responses import FileResponse
12
+
13
+ dashboard_router = APIRouter()
14
+ templates = Jinja2Templates(directory="templates")
15
+
16
+ @dashboard_router.get("/reports", response_class=HTMLResponse)
17
+ async def report_dashboard(request: Request):
18
+ return templates.TemplateResponse("report_dashboard.html", {"request": request})
19
+
20
+ @dashboard_router.get("/reports/health_data")
21
+ async def get_health_data():
22
+ """
23
+ Retrieve health data for dashboard visualization.
24
+ """
25
+ health_collection = db.get_collection("health_records")
26
+ health_data = list(health_collection.find())
27
+ return JSONResponse(health_data)
28
+
29
+ @dashboard_router.get("/reports/activity_data")
30
+ async def get_activity_data():
31
+ """
32
+ Retrieve activity logs for dashboard visualization.
33
+ """
34
+ activity_collection = db.get_collection("activity_logs")
35
+ activity_logs = list(activity_collection.find())
36
+ return JSONResponse(activity_logs)
37
+
38
+ @dashboard_router.get("/export/health/pdf")
39
+ async def export_health_pdf():
40
+ buffer = io.BytesIO()
41
+ pdf = canvas.Canvas(buffer, pagesize=letter)
42
+ pdf.drawString(100, 750, "Health Records Report")
43
+ # Add more data...
44
+ pdf.save()
45
+ buffer.seek(0)
46
+ return StreamingResponse(buffer, media_type="application/pdf", headers={"Content-Disposition": "attachment;filename=health_records.pdf"})
47
+
48
+ @dashboard_router.get("/export/health/xls")
49
+ async def export_health_xls():
50
+ health_data = list(db.get_collection("health_records").find())
51
+ df = pd.DataFrame(health_data)
52
+ df.to_excel("health_records.xlsx", index=False)
53
+ return FileResponse("health_records.xlsx", media_type="application/vnd.ms-excel", headers={"Content-Disposition": "attachment;filename=health_records.xlsx"})
services/utils.py CHANGED
@@ -178,3 +178,18 @@ def test_db_connection():
178
  """
179
  result = mongo_instance.test_connection()
180
  print(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  """
179
  result = mongo_instance.test_connection()
180
  print(result)
181
+
182
+
183
+ log_collection = db.get_collection("activity_logs")
184
+
185
+ def log_activity(activity_type: str, description: str, user_id: str):
186
+ """
187
+ Logs an activity with type, description, and user details.
188
+ """
189
+ log_entry = {
190
+ "activity_type": activity_type,
191
+ "description": description,
192
+ "user_id": user_id,
193
+ "timestamp": datetime.utcnow()
194
+ }
195
+ log_collection.insert_one(log_entry)
templates/report_dashboard.html ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block content %}
4
+ <div class="content-header">
5
+ <div class="container-fluid">
6
+ <h1>Health and Activity Logs</h1>
7
+ </div>
8
+ </div>
9
+
10
+ <div class="content">
11
+ <div class="container-fluid">
12
+ <!-- Health Records Table -->
13
+ <div class="card">
14
+ <div class="card-header">
15
+ <h3 class="card-title">Health Records</h3>
16
+ <div class="card-tools">
17
+ <button onclick="exportHealthData('pdf')" class="btn btn-danger btn-sm">Export PDF</button>
18
+ <button onclick="exportHealthData('xls')" class="btn btn-success btn-sm">Export XLS</button>
19
+ </div>
20
+ </div>
21
+ <div class="card-body table-responsive">
22
+ <table class="table table-hover">
23
+ <thead>
24
+ <tr>
25
+ <th>Record Date</th>
26
+ <th>Metric</th>
27
+ <th>Value</th>
28
+ <th>Status</th>
29
+ </tr>
30
+ </thead>
31
+ <tbody id="health-records">
32
+ <!-- Populate rows via JavaScript -->
33
+ </tbody>
34
+ </table>
35
+ </div>
36
+ </div>
37
+
38
+ <!-- Activity Logs Table -->
39
+ <div class="card mt-4">
40
+ <div class="card-header">
41
+ <h3 class="card-title">Activity Logs</h3>
42
+ <div class="card-tools">
43
+ <button onclick="exportActivityData('pdf')" class="btn btn-danger btn-sm">Export PDF</button>
44
+ <button onclick="exportActivityData('xls')" class="btn btn-success btn-sm">Export XLS</button>
45
+ </div>
46
+ </div>
47
+ <div class="card-body table-responsive">
48
+ <table class="table table-hover">
49
+ <thead>
50
+ <tr>
51
+ <th>Timestamp</th>
52
+ <th>Type</th>
53
+ <th>Description</th>
54
+ <th>User</th>
55
+ </tr>
56
+ </thead>
57
+ <tbody id="activity-logs">
58
+ <!-- Populate rows via JavaScript -->
59
+ </tbody>
60
+ </table>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ <!-- Add in report_dashboard.html -->
66
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
67
+ <script>
68
+ async function loadHealthData() {
69
+ const response = await fetch("/reports/health_data");
70
+ const data = await response.json();
71
+ // Process data for Chart.js format
72
+
73
+ const ctx = document.getElementById("healthChart").getContext("2d");
74
+ new Chart(ctx, {
75
+ type: 'line',
76
+ data: {
77
+ labels: data.map(entry => entry.date),
78
+ datasets: [{
79
+ label: 'Mortality Rate',
80
+ data: data.map(entry => entry.mortality_rate),
81
+ borderColor: 'rgb(255, 99, 132)',
82
+ fill: false
83
+ }]
84
+ },
85
+ options: {
86
+ scales: {
87
+ x: { type: 'time', time: { unit: 'day' } },
88
+ y: { beginAtZero: true }
89
+ }
90
+ }
91
+ });
92
+ }
93
+ document.addEventListener("DOMContentLoaded", loadHealthData);
94
+ </script>
95
+
96
+
97
+ {% endblock %}