Meet Patel commited on
Commit
3d56f3d
·
1 Parent(s): 411f252

Removed gamification features and deployment configuration

Browse files
README.md CHANGED
@@ -182,16 +182,6 @@ TutorX-MCP can integrate with various external educational systems:
182
  - Schedule and manage tutoring sessions
183
  - Connect students with expert tutors
184
 
185
- ## Deployment
186
-
187
- For production deployment, see [Deployment Guide](docs/deployment.md) which covers:
188
-
189
- - Docker-based deployment
190
- - Manual installation
191
- - Scaling strategies
192
- - Monitoring setup
193
- - Security considerations
194
-
195
  ## Documentation
196
 
197
  - [API Documentation](docs/api.md): Complete API reference for developers
 
182
  - Schedule and manage tutoring sessions
183
  - Connect students with expert tutors
184
 
 
 
 
 
 
 
 
 
 
 
185
  ## Documentation
186
 
187
  - [API Documentation](docs/api.md): Complete API reference for developers
app.py CHANGED
@@ -56,7 +56,7 @@ with gr.Blocks(title="TutorX Educational AI", theme=gr.themes.Soft()) as demo:
56
 
57
  with gr.Column():
58
  assessment_output = gr.JSON(label="Skill Assessment")
59
- assess_btn.click(
60
  fn=lambda concept: client.assess_skill(student_id, concept),
61
  inputs=[concept_id_input],
62
  outputs=[assessment_output]
@@ -105,7 +105,7 @@ with gr.Blocks(title="TutorX Educational AI", theme=gr.themes.Soft()) as demo:
105
 
106
  with gr.Column():
107
  lesson_output = gr.JSON(label="Lesson Plan")
108
- gen_lesson_btn.click(
109
  fn=lambda topic, grade, duration: client.generate_lesson(topic, grade, duration),
110
  inputs=[topic_input, grade_input, duration_input],
111
  outputs=[lesson_output]
@@ -142,7 +142,7 @@ with gr.Blocks(title="TutorX Educational AI", theme=gr.themes.Soft()) as demo:
142
 
143
  with gr.Column():
144
  text_output = gr.JSON(label="Response")
145
- text_btn.click(
146
  fn=lambda query: client.text_interaction(query, student_id),
147
  inputs=[text_input],
148
  outputs=[text_output]
@@ -171,7 +171,7 @@ with gr.Blocks(title="TutorX Educational AI", theme=gr.themes.Soft()) as demo:
171
  analytics_btn = gr.Button("Generate Analytics Report")
172
  timeframe = gr.Slider(minimum=7, maximum=90, value=30, step=1, label="Timeframe (days)")
173
  analytics_output = gr.JSON(label="Performance Analytics")
174
- analytics_btn.click(
175
  fn=lambda days: client.get_student_analytics(student_id, days),
176
  inputs=[timeframe],
177
  outputs=[analytics_output]
@@ -210,7 +210,7 @@ with gr.Blocks(title="TutorX Educational AI", theme=gr.themes.Soft()) as demo:
210
 
211
  with gr.Column():
212
  assessment_output = gr.JSON(label="Generated Assessment")
213
- create_assess_btn.click(
214
  fn=lambda concepts, num, diff: client.create_assessment(concepts, num, diff),
215
  inputs=[assess_concepts, assess_questions, assess_diff],
216
  outputs=[assessment_output]
@@ -240,102 +240,6 @@ with gr.Blocks(title="TutorX Educational AI", theme=gr.themes.Soft()) as demo:
240
  inputs=[submission_input, reference_input],
241
  outputs=[plagiarism_output]
242
  )
243
-
244
- # Tab 6: Gamification
245
- with gr.Tab("Gamification"):
246
- gr.Markdown("## Student Badges")
247
-
248
- with gr.Row():
249
- with gr.Column():
250
- badges_student_id = gr.Textbox(label="Student ID", value=student_id)
251
- get_badges_btn = gr.Button("Get Student Badges")
252
-
253
- with gr.Column():
254
- badges_output = gr.JSON(label="Student Badges")
255
-
256
- get_badges_btn.click(
257
- fn=lambda sid: client.get_badges_for_student(sid),
258
- inputs=[badges_student_id],
259
- outputs=[badges_output]
260
- )
261
-
262
- gr.Markdown("## Award Badge")
263
-
264
- with gr.Row():
265
- with gr.Column():
266
- award_student_id = gr.Textbox(label="Student ID", value=student_id)
267
- badge_id = gr.Dropdown(
268
- choices=["beginner", "persistent", "math_whiz", "science_explorer",
269
- "speed_demon", "accuracy_master", "helping_hand",
270
- "night_owl", "early_bird", "perfect_streak"],
271
- label="Badge to Award",
272
- value="beginner"
273
- )
274
- award_badge_btn = gr.Button("Award Badge")
275
-
276
- with gr.Column():
277
- award_output = gr.JSON(label="Award Result")
278
-
279
- award_badge_btn.click(
280
- fn=lambda sid, bid: client.award_student_badge(sid, bid),
281
- inputs=[award_student_id, badge_id],
282
- outputs=[award_output]
283
- )
284
-
285
- gr.Markdown("## Leaderboards")
286
-
287
- with gr.Row():
288
- with gr.Column():
289
- leaderboard_id = gr.Dropdown(
290
- choices=["weekly_points", "monthly_streak", "problem_solving_speed"],
291
- label="Leaderboard",
292
- value="weekly_points"
293
- )
294
- get_leaderboard_btn = gr.Button("Get Leaderboard")
295
-
296
- with gr.Column():
297
- leaderboard_output = gr.JSON(label="Leaderboard")
298
-
299
- get_leaderboard_btn.click(
300
- fn=lambda lid: client.get_current_leaderboard(lid),
301
- inputs=[leaderboard_id],
302
- outputs=[leaderboard_output]
303
- )
304
-
305
- gr.Markdown("## Track Activity")
306
-
307
- with gr.Row():
308
- with gr.Column():
309
- track_student_id = gr.Textbox(label="Student ID", value=student_id)
310
- activity_type = gr.Dropdown(
311
- choices=["lesson_completed", "assessment_completed", "problem_solved", "forum_post", "login"],
312
- label="Activity Type",
313
- value="lesson_completed"
314
- )
315
- activity_score = gr.Slider(minimum=0, maximum=1, value=0.85, step=0.01, label="Score/Performance")
316
- activity_subject = gr.Dropdown(
317
- choices=["math", "science", "language", "history", "other"],
318
- label="Subject",
319
- value="math"
320
- )
321
- track_btn = gr.Button("Track Activity")
322
-
323
- with gr.Column():
324
- activity_output = gr.JSON(label="Activity Tracking Result")
325
-
326
- track_btn.click(
327
- fn=lambda sid, atype, score, subject: client.track_student_activity(
328
- sid,
329
- {
330
- "activity_type": atype,
331
- "score": score,
332
- "subject": subject,
333
- "time_seconds": 90 # Simulated time value
334
- }
335
- ),
336
- inputs=[track_student_id, activity_type, activity_score, activity_subject],
337
- outputs=[activity_output]
338
- )
339
 
340
  # Launch the app
341
  if __name__ == "__main__":
 
56
 
57
  with gr.Column():
58
  assessment_output = gr.JSON(label="Skill Assessment")
59
+ assess_btn.click(
60
  fn=lambda concept: client.assess_skill(student_id, concept),
61
  inputs=[concept_id_input],
62
  outputs=[assessment_output]
 
105
 
106
  with gr.Column():
107
  lesson_output = gr.JSON(label="Lesson Plan")
108
+ gen_lesson_btn.click(
109
  fn=lambda topic, grade, duration: client.generate_lesson(topic, grade, duration),
110
  inputs=[topic_input, grade_input, duration_input],
111
  outputs=[lesson_output]
 
142
 
143
  with gr.Column():
144
  text_output = gr.JSON(label="Response")
145
+ text_btn.click(
146
  fn=lambda query: client.text_interaction(query, student_id),
147
  inputs=[text_input],
148
  outputs=[text_output]
 
171
  analytics_btn = gr.Button("Generate Analytics Report")
172
  timeframe = gr.Slider(minimum=7, maximum=90, value=30, step=1, label="Timeframe (days)")
173
  analytics_output = gr.JSON(label="Performance Analytics")
174
+ analytics_btn.click(
175
  fn=lambda days: client.get_student_analytics(student_id, days),
176
  inputs=[timeframe],
177
  outputs=[analytics_output]
 
210
 
211
  with gr.Column():
212
  assessment_output = gr.JSON(label="Generated Assessment")
213
+ create_assess_btn.click(
214
  fn=lambda concepts, num, diff: client.create_assessment(concepts, num, diff),
215
  inputs=[assess_concepts, assess_questions, assess_diff],
216
  outputs=[assessment_output]
 
240
  inputs=[submission_input, reference_input],
241
  outputs=[plagiarism_output]
242
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
  # Launch the app
245
  if __name__ == "__main__":
client.py CHANGED
@@ -191,50 +191,13 @@ class TutorXClient:
191
  "student_id": student_id,
192
  "timeframe_days": timeframe_days
193
  })
194
-
195
- def check_submission_originality(self, submission: str, reference_sources: List[str]) -> Dict[str, Any]:
196
  """Check student submission for potential plagiarism"""
197
  return self._call_tool("check_submission_originality", {
198
  "submission": submission,
199
  "reference_sources": reference_sources
200
  })
201
 
202
- # ------------ Gamification ------------
203
-
204
- def award_student_badge(self, student_id: str, badge_id: str) -> Dict[str, Any]:
205
- """Award a badge to a student"""
206
- return self._call_tool("award_student_badge", {
207
- "student_id": student_id,
208
- "badge_id": badge_id
209
- })
210
-
211
- def get_badges_for_student(self, student_id: str) -> Dict[str, Any]:
212
- """Get all badges for a student"""
213
- return self._call_tool("get_badges_for_student", {
214
- "student_id": student_id
215
- })
216
-
217
- def update_student_leaderboard(self, leaderboard_id: str, student_id: str, score: float) -> Dict[str, Any]:
218
- """Update a leaderboard with a student's score"""
219
- return self._call_tool("update_student_leaderboard", {
220
- "leaderboard_id": leaderboard_id,
221
- "student_id": student_id,
222
- "score": score
223
- })
224
-
225
- def get_current_leaderboard(self, leaderboard_id: str) -> Dict[str, Any]:
226
- """Get current leaderboard standings"""
227
- return self._call_tool("get_current_leaderboard", {
228
- "leaderboard_id": leaderboard_id
229
- })
230
-
231
- def track_student_activity(self, student_id: str, activity_data: Dict[str, Any]) -> Dict[str, Any]:
232
- """Track a student's activity and check for achievements"""
233
- return self._call_tool("track_student_activity", {
234
- "student_id": student_id,
235
- "activity_data": activity_data
236
- })
237
-
238
  # ------------ External Integrations ------------
239
 
240
  def lms_sync_grades(self, lms_type: str, api_url: str, api_key: str,
 
191
  "student_id": student_id,
192
  "timeframe_days": timeframe_days
193
  })
194
+ def check_submission_originality(self, submission: str, reference_sources: List[str]) -> Dict[str, Any]:
 
195
  """Check student submission for potential plagiarism"""
196
  return self._call_tool("check_submission_originality", {
197
  "submission": submission,
198
  "reference_sources": reference_sources
199
  })
200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  # ------------ External Integrations ------------
202
 
203
  def lms_sync_grades(self, lms_type: str, api_url: str, api_key: str,
deployment/Dockerfile DELETED
@@ -1,19 +0,0 @@
1
- FROM python:3.12-slim
2
-
3
- WORKDIR /app
4
-
5
- # Install dependencies
6
- COPY pyproject.toml .
7
- RUN pip install --no-cache-dir uv && \
8
- uv pip install --no-cache -e .
9
-
10
- # Copy application code
11
- COPY main.py .
12
- COPY client.py .
13
- COPY utils/ ./utils/
14
-
15
- # Expose port for MCP server
16
- EXPOSE 8000
17
-
18
- # Run MCP server
19
- CMD ["python", "main.py"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
deployment/Dockerfile.web DELETED
@@ -1,19 +0,0 @@
1
- FROM python:3.12-slim
2
-
3
- WORKDIR /app
4
-
5
- # Install dependencies
6
- COPY pyproject.toml .
7
- RUN pip install --no-cache-dir uv && \
8
- uv pip install --no-cache -e .
9
-
10
- # Copy application code
11
- COPY app.py .
12
- COPY client.py .
13
- COPY utils/ ./utils/
14
-
15
- # Expose port for Gradio interface
16
- EXPOSE 7860
17
-
18
- # Run Gradio interface
19
- CMD ["python", "app.py"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
deployment/docker-compose.yml DELETED
@@ -1,46 +0,0 @@
1
- version: '3'
2
-
3
- services:
4
- tutorx-mcp:
5
- build:
6
- context: ..
7
- dockerfile: deployment/Dockerfile
8
- ports:
9
- - "8000:8000" # MCP server
10
- environment:
11
- - MCP_HOST=0.0.0.0
12
- - MCP_PORT=8000
13
- - LOG_LEVEL=INFO
14
- - REDIS_HOST=redis
15
- volumes:
16
- - tutorx-data:/data
17
- depends_on:
18
- - redis
19
- restart: unless-stopped
20
-
21
- tutorx-web:
22
- build:
23
- context: ..
24
- dockerfile: deployment/Dockerfile.web
25
- ports:
26
- - "7860:7860" # Gradio interface
27
- environment:
28
- - GRADIO_SERVER_NAME=0.0.0.0
29
- - GRADIO_SERVER_PORT=7860
30
- - MCP_SERVER_URL=http://tutorx-mcp:8000
31
- depends_on:
32
- - tutorx-mcp
33
- restart: unless-stopped
34
-
35
- redis:
36
- image: redis:alpine
37
- ports:
38
- - "6379:6379"
39
- volumes:
40
- - redis-data:/data
41
- command: redis-server --appendonly yes
42
- restart: unless-stopped
43
-
44
- volumes:
45
- tutorx-data:
46
- redis-data:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/deployment.md DELETED
@@ -1,85 +0,0 @@
1
- # TutorX-MCP Deployment Guide
2
-
3
- This guide provides instructions for deploying the TutorX-MCP server in production environments.
4
-
5
- ## Deployment Options
6
-
7
- ### 1. Docker Deployment (Recommended)
8
-
9
- The easiest way to deploy TutorX-MCP is using Docker and Docker Compose:
10
-
11
- ```bash
12
- # Navigate to the deployment directory
13
- cd deployment
14
-
15
- # Start the services
16
- docker-compose up -d
17
- ```
18
-
19
- This will start:
20
- - The MCP server at http://localhost:8000
21
- - The Gradio web interface at http://localhost:7860
22
- - A Redis instance for caching and session management
23
-
24
- ### 2. Manual Deployment
25
-
26
- #### Prerequisites
27
-
28
- - Python 3.12 or higher
29
- - Redis (optional, but recommended for production)
30
-
31
- #### Steps
32
-
33
- 1. Install dependencies:
34
- ```bash
35
- uv install -e .
36
- ```
37
-
38
- 2. Configure environment variables:
39
- ```bash
40
- # Server configuration
41
- export MCP_HOST=0.0.0.0
42
- export MCP_PORT=8000
43
-
44
- # Redis configuration (if using)
45
- export REDIS_HOST=localhost
46
- export REDIS_PORT=6379
47
- ```
48
-
49
- 3. Run the server:
50
- ```bash
51
- python run.py --mode both --host 0.0.0.0
52
- ```
53
-
54
- ## Scaling
55
-
56
- For high-traffic deployments, consider:
57
-
58
- 1. Using a reverse proxy like Nginx or Traefik in front of the services
59
- 2. Implementing load balancing for multiple MCP server instances
60
- 3. Scaling the Redis cache using Redis Sentinel or Redis Cluster
61
-
62
- ## Monitoring
63
-
64
- We recommend setting up:
65
-
66
- 1. Prometheus for metrics collection
67
- 2. Grafana for visualization
68
- 3. ELK stack for log management
69
-
70
- ## Security Considerations
71
-
72
- 1. In production, always use HTTPS
73
- 2. Implement proper authentication for API access
74
- 3. Keep dependencies updated
75
- 4. Follow least privilege principles for service accounts
76
-
77
- ## Environment Variables
78
-
79
- | Variable | Description | Default |
80
- |----------|-------------|---------|
81
- | MCP_HOST | Host address for MCP server | 127.0.0.1 |
82
- | MCP_PORT | Port for MCP server | 8000 |
83
- | REDIS_HOST | Redis host address | localhost |
84
- | REDIS_PORT | Redis port | 6379 |
85
- | LOG_LEVEL | Logging level (DEBUG, INFO, WARNING, ERROR) | INFO |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
main.py CHANGED
@@ -18,13 +18,6 @@ from utils.assessment import (
18
  generate_performance_analytics,
19
  detect_plagiarism
20
  )
21
- from utils.gamification import (
22
- award_badge,
23
- get_student_badges,
24
- update_leaderboard,
25
- get_leaderboard,
26
- check_achievements
27
- )
28
  from utils.integrations import (
29
  LMSIntegration,
30
  OERIntegration,
@@ -588,89 +581,9 @@ def check_submission_originality(submission: str, reference_sources: List[str])
588
  reference_sources: List of reference texts to check against
589
 
590
  Returns:
591
- Originality analysis
592
- """
593
  return detect_plagiarism(submission, reference_sources)
594
 
595
- # ------------------ Gamification Features ------------------
596
-
597
- @mcp.tool()
598
- def award_student_badge(student_id: str, badge_id: str) -> Dict[str, Any]:
599
- """
600
- Award a badge to a student
601
-
602
- Args:
603
- student_id: The student's unique identifier
604
- badge_id: The badge ID to award
605
-
606
- Returns:
607
- Badge information
608
- """
609
- return award_badge(student_id, badge_id)
610
-
611
- @mcp.tool()
612
- def get_badges_for_student(student_id: str) -> Dict[str, Any]:
613
- """
614
- Get all badges for a student
615
-
616
- Args:
617
- student_id: The student's unique identifier
618
-
619
- Returns:
620
- Badge information
621
- """
622
- return get_student_badges(student_id)
623
-
624
- @mcp.tool()
625
- def update_student_leaderboard(leaderboard_id: str, student_id: str, score: float) -> Dict[str, Any]:
626
- """
627
- Update a leaderboard with a student's score
628
-
629
- Args:
630
- leaderboard_id: ID of the leaderboard to update
631
- student_id: The student's unique identifier
632
- score: The score to record
633
-
634
- Returns:
635
- Leaderboard information
636
- """
637
- return update_leaderboard(leaderboard_id, student_id, score)
638
-
639
- @mcp.tool()
640
- def get_current_leaderboard(leaderboard_id: str) -> Dict[str, Any]:
641
- """
642
- Get current leaderboard standings
643
-
644
- Args:
645
- leaderboard_id: ID of the leaderboard to get
646
-
647
- Returns:
648
- Leaderboard information
649
- """
650
- return get_leaderboard(leaderboard_id)
651
-
652
- @mcp.tool()
653
- def track_student_activity(student_id: str, activity_data: Dict[str, Any]) -> Dict[str, Any]:
654
- """
655
- Track a student's activity and check for achievements
656
-
657
- Args:
658
- student_id: The student's unique identifier
659
- activity_data: Data about the activity
660
-
661
- Returns:
662
- Tracking information and any new badges
663
- """
664
- new_badges = check_achievements(student_id, activity_data)
665
-
666
- return {
667
- "student_id": student_id,
668
- "activity_tracked": True,
669
- "activity_data": activity_data,
670
- "new_badges": new_badges,
671
- "timestamp": datetime.now().isoformat()
672
- }
673
-
674
  # ------------------ External Integrations ------------------
675
 
676
  @mcp.tool()
 
18
  generate_performance_analytics,
19
  detect_plagiarism
20
  )
 
 
 
 
 
 
 
21
  from utils.integrations import (
22
  LMSIntegration,
23
  OERIntegration,
 
581
  reference_sources: List of reference texts to check against
582
 
583
  Returns:
584
+ Originality analysis """
 
585
  return detect_plagiarism(submission, reference_sources)
586
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587
  # ------------------ External Integrations ------------------
588
 
589
  @mcp.tool()
utils/gamification.py DELETED
@@ -1,306 +0,0 @@
1
- """
2
- Gamification utilities for the TutorX MCP server
3
- """
4
-
5
- from typing import Dict, Any, List, Optional
6
- from datetime import datetime
7
- import random
8
-
9
-
10
- # Dictionary to store badges for students
11
- # In a real application, this would be stored in a database
12
- BADGES_DB = {}
13
-
14
- # Dictionary to store leaderboards
15
- # In a real application, this would be stored in a database
16
- LEADERBOARDS_DB = {
17
- "weekly_points": {},
18
- "monthly_streak": {},
19
- "problem_solving_speed": {}
20
- }
21
-
22
- # Badge definitions
23
- BADGES = {
24
- "beginner": {
25
- "name": "Beginner",
26
- "description": "Completed your first lesson",
27
- "icon": "🔰",
28
- "points": 10
29
- },
30
- "persistent": {
31
- "name": "Persistent Learner",
32
- "description": "Completed 5 lessons in a row",
33
- "icon": "🔄",
34
- "points": 25
35
- },
36
- "math_whiz": {
37
- "name": "Math Whiz",
38
- "description": "Scored 100% on a math assessment",
39
- "icon": "🧮",
40
- "points": 50
41
- },
42
- "science_explorer": {
43
- "name": "Science Explorer",
44
- "description": "Completed 10 science modules",
45
- "icon": "🔬",
46
- "points": 50
47
- },
48
- "speed_demon": {
49
- "name": "Speed Demon",
50
- "description": "Solved 5 problems in under 2 minutes each",
51
- "icon": "⚡",
52
- "points": 35
53
- },
54
- "accuracy_master": {
55
- "name": "Accuracy Master",
56
- "description": "Maintained 90% accuracy over 20 problems",
57
- "icon": "🎯",
58
- "points": 40
59
- },
60
- "helping_hand": {
61
- "name": "Helping Hand",
62
- "description": "Helped 5 other students in the forum",
63
- "icon": "🤝",
64
- "points": 30
65
- },
66
- "night_owl": {
67
- "name": "Night Owl",
68
- "description": "Studied for 3 hours after 8 PM",
69
- "icon": "🌙",
70
- "points": 20
71
- },
72
- "early_bird": {
73
- "name": "Early Bird",
74
- "description": "Studied for 3 hours before 9 AM",
75
- "icon": "🌅",
76
- "points": 20
77
- },
78
- "perfect_streak": {
79
- "name": "Perfect Streak",
80
- "description": "Logged in for 7 days straight",
81
- "icon": "🔥",
82
- "points": 45
83
- }
84
- }
85
-
86
-
87
- def award_badge(student_id: str, badge_id: str) -> Dict[str, Any]:
88
- """
89
- Award a badge to a student
90
-
91
- Args:
92
- student_id: The student's unique identifier
93
- badge_id: The badge's unique identifier
94
-
95
- Returns:
96
- Badge information
97
- """
98
- if badge_id not in BADGES:
99
- return {
100
- "error": "Invalid badge ID",
101
- "timestamp": datetime.now().isoformat()
102
- }
103
-
104
- if student_id not in BADGES_DB:
105
- BADGES_DB[student_id] = {}
106
-
107
- # Check if student already has the badge
108
- if badge_id in BADGES_DB[student_id]:
109
- return {
110
- "message": "Badge already awarded",
111
- "badge": BADGES[badge_id],
112
- "timestamp": BADGES_DB[student_id][badge_id]["timestamp"]
113
- }
114
-
115
- # Award the badge
116
- BADGES_DB[student_id][badge_id] = {
117
- "timestamp": datetime.now().isoformat()
118
- }
119
-
120
- return {
121
- "message": "Badge awarded!",
122
- "badge": BADGES[badge_id],
123
- "timestamp": datetime.now().isoformat()
124
- }
125
-
126
-
127
- def get_student_badges(student_id: str) -> Dict[str, Any]:
128
- """
129
- Get all badges for a student
130
-
131
- Args:
132
- student_id: The student's unique identifier
133
-
134
- Returns:
135
- Dictionary of badges and points
136
- """
137
- if student_id not in BADGES_DB or not BADGES_DB[student_id]:
138
- return {
139
- "student_id": student_id,
140
- "badges": [],
141
- "total_points": 0,
142
- "timestamp": datetime.now().isoformat()
143
- }
144
-
145
- badges_list = []
146
- total_points = 0
147
-
148
- for badge_id in BADGES_DB[student_id]:
149
- if badge_id in BADGES:
150
- badge_info = BADGES[badge_id].copy()
151
- badge_info["awarded_at"] = BADGES_DB[student_id][badge_id]["timestamp"]
152
- badges_list.append(badge_info)
153
- total_points += badge_info["points"]
154
-
155
- return {
156
- "student_id": student_id,
157
- "badges": badges_list,
158
- "total_points": total_points,
159
- "timestamp": datetime.now().isoformat()
160
- }
161
-
162
-
163
- def update_leaderboard(leaderboard_id: str, student_id: str, score: float) -> Dict[str, Any]:
164
- """
165
- Update a leaderboard with a new score for a student
166
-
167
- Args:
168
- leaderboard_id: The leaderboard to update
169
- student_id: The student's unique identifier
170
- score: The score to record
171
-
172
- Returns:
173
- Updated leaderboard information
174
- """
175
- if leaderboard_id not in LEADERBOARDS_DB:
176
- return {
177
- "error": "Invalid leaderboard ID",
178
- "timestamp": datetime.now().isoformat()
179
- }
180
-
181
- # Update student's score
182
- LEADERBOARDS_DB[leaderboard_id][student_id] = {
183
- "score": score,
184
- "timestamp": datetime.now().isoformat()
185
- }
186
-
187
- # Get top 10 students
188
- top_students = sorted(
189
- LEADERBOARDS_DB[leaderboard_id].items(),
190
- key=lambda x: x[1]["score"],
191
- reverse=True
192
- )[:10]
193
-
194
- # Format leaderboard
195
- leaderboard = []
196
- for i, (sid, data) in enumerate(top_students):
197
- leaderboard.append({
198
- "rank": i + 1,
199
- "student_id": sid,
200
- "score": data["score"],
201
- "last_updated": data["timestamp"]
202
- })
203
-
204
- # Find student's rank
205
- student_rank = next(
206
- (i + 1 for i, (sid, _) in enumerate(top_students) if sid == student_id),
207
- len(LEADERBOARDS_DB[leaderboard_id]) + 1
208
- )
209
-
210
- return {
211
- "leaderboard_id": leaderboard_id,
212
- "leaderboard": leaderboard,
213
- "student_rank": student_rank,
214
- "student_score": score,
215
- "timestamp": datetime.now().isoformat()
216
- }
217
-
218
-
219
- def get_leaderboard(leaderboard_id: str) -> Dict[str, Any]:
220
- """
221
- Get the current state of a leaderboard
222
-
223
- Args:
224
- leaderboard_id: The leaderboard to get
225
-
226
- Returns:
227
- Leaderboard information
228
- """
229
- if leaderboard_id not in LEADERBOARDS_DB:
230
- return {
231
- "error": "Invalid leaderboard ID",
232
- "timestamp": datetime.now().isoformat()
233
- }
234
-
235
- # Get top 10 students
236
- top_students = sorted(
237
- LEADERBOARDS_DB[leaderboard_id].items(),
238
- key=lambda x: x[1]["score"],
239
- reverse=True
240
- )[:10]
241
-
242
- # Format leaderboard
243
- leaderboard = []
244
- for i, (sid, data) in enumerate(top_students):
245
- leaderboard.append({
246
- "rank": i + 1,
247
- "student_id": sid,
248
- "score": data["score"],
249
- "last_updated": data["timestamp"]
250
- })
251
-
252
- return {
253
- "leaderboard_id": leaderboard_id,
254
- "leaderboard": leaderboard,
255
- "total_students": len(LEADERBOARDS_DB[leaderboard_id]),
256
- "timestamp": datetime.now().isoformat()
257
- }
258
-
259
-
260
- def check_achievements(student_id: str, activity_data: Dict[str, Any]) -> List[Dict[str, Any]]:
261
- """
262
- Check if a student's activity unlocks any new badges
263
-
264
- Args:
265
- student_id: The student's unique identifier
266
- activity_data: Data about the student's activity
267
-
268
- Returns:
269
- List of newly awarded badges
270
- """
271
- new_badges = []
272
-
273
- # Initialize student badge record if needed
274
- if student_id not in BADGES_DB:
275
- BADGES_DB[student_id] = {}
276
-
277
- # Check for potential badge earnings based on activity
278
- if "activity_type" in activity_data:
279
- activity_type = activity_data["activity_type"]
280
-
281
- # Beginner badge - first lesson
282
- if activity_type == "lesson_completed" and "beginner" not in BADGES_DB[student_id]:
283
- badge_result = award_badge(student_id, "beginner")
284
- if "error" not in badge_result:
285
- new_badges.append(badge_result)
286
-
287
- # Math Whiz - perfect math assessment
288
- if (activity_type == "assessment_completed" and
289
- activity_data.get("subject") == "math" and
290
- activity_data.get("score") == 1.0 and
291
- "math_whiz" not in BADGES_DB[student_id]):
292
- badge_result = award_badge(student_id, "math_whiz")
293
- if "error" not in badge_result:
294
- new_badges.append(badge_result)
295
-
296
- # Speed Demon - fast problem solving
297
- if (activity_type == "problem_solved" and
298
- activity_data.get("time_seconds", 999) < 120):
299
- # In a real system, we'd track the count over time
300
- # Here we'll simulate it
301
- if random.random() < 0.2 and "speed_demon" not in BADGES_DB[student_id]:
302
- badge_result = award_badge(student_id, "speed_demon")
303
- if "error" not in badge_result:
304
- new_badges.append(badge_result)
305
-
306
- return new_badges