Compare commits

..

61 Commits

Author SHA1 Message Date
ayush 3a4cfbc897 ayush 6 months ago
ayush 8860907d80 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
ayush 92ae578bdb ayush 6 months ago
Kushal Dotel 8dd5b8fca2 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
Kushal Dotel 0a17fbba45 fix init 6 months ago
ayush 9f19445301 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
ayush 3f777b170d ayush 6 months ago
Kushal Dotel 88b4537b4a upload readme.md 2 6 months ago
Kushal Dotel 15accb4385 upload readme.md 6 months ago
Kushal Dotel cd9253b19b upload readme.md 6 months ago
Kushal Dotel 1c85702154 fix: cors 6 months ago
Kushal Dotel 5041d84363 updated the quiz 6 months ago
Kushal Dotel ac5813a0de Fix Backend 6 months ago
Kushal Dotel 8d0ae7769a Fix Backend 6 months ago
Kushal Dotel c603e293da Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
Kushal Dotel 5abe922b98 Fix Backend 6 months ago
Casu Al Snek 31caf3d052 Fix syntax errors 6 months ago
Casu Al Snek 8c029cbe14 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
Casu Al Snek 607902fd48 Implement quiz API and badge API 6 months ago
ayush db1c835920 ayush 6 months ago
I-am-Stone e4877a54e3 categorytable 6 months ago
ayush 4b622f15c3 ayush 6 months ago
I-am-Stone 5db8102fec admin category, course 6 months ago
Kushal Dotel 3741dbc38a continue in my own 6 months ago
ayush 716e5cffb4 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
ayush f16bb5862e user table 6 months ago
Casu Al Snek 4169e265ab Ft: Allow admin mods from profile API 6 months ago
Casu Al Snek da3204380d Ft: Add self marker in chat message get request 6 months ago
Casu Al Snek 8113aa2051 Fix base query /listAll 6 months ago
Casu Al Snek 5ab4e24ece Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
Casu Al Snek 2d6dfb0695 Allow show pending in /listAll when authenticated as admin 6 months ago
Kushal Dotel 8b7fe2dc6c update the chats 6 months ago
Kushal Dotel c16cf0f216 update the chat 6 months ago
Casu Al Snek bec6c10e08 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
Casu Al Snek d1e64a7eca Implement chat api 6 months ago
Kushal Dotel 7f3ba4fb65 roll back course init 6 months ago
Kushal Dotel 14789498f0 update the dbs 6 months ago
Kushal Dotel f477cc5577 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
Casu Al Snek 4dc8d6ab64 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
Casu Al Snek e6122c3536 Implement course APIs 2 6 months ago
Kushal Dotel 4d0d8b94f6 fixes 6 months ago
Kushal Dotel 187754261f fix course init 6 months ago
Kushal Dotel 8c46f2a61a Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
Kushal Dotel 5a29dff433 update: uploads 6 months ago
Casu Al Snek 3dbbeebd59 Implement course APIs 6 months ago
Kushal Dotel ef35036bc5 public api schema 6 months ago
ayush 057e9c9043 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
ayush a80995b170 doing change password 6 months ago
Kushal Dotel a6d03971eb fix:chat 6 months ago
Kushal Dotel 7852c8c559 send message to the courses 6 months ago
Kushal Dotel c51d14f1b0 feat: add CORS policy 6 months ago
Kushal Dotel 8016d489bd feat: admin dashboard and stats 6 months ago
ayush 6afb2a7cfb Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
ayush e3ffa28c35 course detail page 6 months ago
Casu Al Snek e70d869b76 Allow int casting of enums 6 months ago
Casu Al Snek b8bbe33815 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
ayush 43b82377e9 Merge branch 'main' of https://hackethon.ai/hack/HackXlbef/FreeBug 6 months ago
ayush 6d9cb1bbb3 user profile ui 6 months ago
Casu Al Snek 2520dc3cce Fix course updates and deletes 6 months ago
Kushal Dotel 7a5ff71010 update normal profile 6 months ago
Kushal Dotel 99c0411370 feat: add change password 6 months ago
  1. 1
      .gitignore
  2. 54
      README.md
  3. 34
      backend/app.py
  4. 39
      backend/blueprints/admin/__init__.py
  5. 192
      backend/blueprints/badge/__init__.py
  6. 172
      backend/blueprints/chat/__init__.py
  7. 541
      backend/blueprints/course/__init__.py
  8. 109
      backend/blueprints/notification/__init__.py
  9. 140
      backend/blueprints/profile/__init__.py
  10. 2
      backend/blueprints/public/__init__.py
  11. 264
      backend/blueprints/quiz/__init__.py
  12. 1
      backend/blueprints/session/__init__.py
  13. 4
      backend/config.py
  14. 6
      backend/constants/__init__.py
  15. 16
      backend/db/model.py
  16. BIN
      backend/requirements.txt
  17. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf
  18. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/1.pdf
  19. 34
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/1.txt
  20. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/2.pdf
  21. 45
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/2.txt
  22. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/3.pdf
  23. 63
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/3.txt
  24. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/4.pdf
  25. 128
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/4.txt
  26. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/5.pdf
  27. 129
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/5.txt
  28. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/6.pdf
  29. 15
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/6.txt
  30. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf
  31. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/1.pdf
  32. 34
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/1.txt
  33. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/2.pdf
  34. 45
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/2.txt
  35. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/3.pdf
  36. 63
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/3.txt
  37. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/4.pdf
  38. 128
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/4.txt
  39. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/5.pdf
  40. 129
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/5.txt
  41. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/6.pdf
  42. 15
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/6.txt
  43. BIN
      backend/uploads/JRyiqfKMGnWcunvfcosPJxgBGQTjasLh.jpg
  44. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf
  45. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/1.pdf
  46. 34
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/1.txt
  47. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/2.pdf
  48. 45
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/2.txt
  49. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/3.pdf
  50. 63
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/3.txt
  51. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/4.pdf
  52. 128
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/4.txt
  53. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/5.pdf
  54. 129
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/5.txt
  55. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/6.pdf
  56. 15
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/6.txt
  57. BIN
      backend/uploads/ToasQcxJytcIEzWUREfvPfkVEmFAYPRT.jpeg
  58. BIN
      backend/uploads/ZFRQjJYWCKWLQQSpPJRZRcAvGFtqzFBd.jpeg
  59. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf
  60. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/1.pdf
  61. 34
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/1.txt
  62. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/2.pdf
  63. 45
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/2.txt
  64. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/3.pdf
  65. 63
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/3.txt
  66. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/4.pdf
  67. 128
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/4.txt
  68. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/5.pdf
  69. 129
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/5.txt
  70. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/6.pdf
  71. 15
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/6.txt
  72. BIN
      backend/uploads/bIhlRmHCOUumtwXCFBbVipXOEvgFvBNN.jpeg
  73. BIN
      backend/uploads/cTWekssdRoWYgpXYaHJsyBPtKwdbeBOL.jpeg
  74. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf
  75. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/1.pdf
  76. 34
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/1.txt
  77. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/2.pdf
  78. 45
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/2.txt
  79. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/3.pdf
  80. 63
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/3.txt
  81. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/4.pdf
  82. 128
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/4.txt
  83. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/5.pdf
  84. 129
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/5.txt
  85. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/6.pdf
  86. 15
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/6.txt
  87. BIN
      backend/uploads/izvjgAZCUwVlTZpoMdoFUnMoMoPNDkPD.pdf
  88. BIN
      backend/uploads/learn-with-us.jpg
  89. BIN
      backend/uploads/uBYfyzaCrsAeHTYdKnPfmVVcahWaTVte.jpeg
  90. BIN
      backend/uploads/user_3e7bb78f-a0e8-42f2-bd9f-6792369e3e64_meme.jpg
  91. BIN
      backend/uploads/user_f7ab56f1-d692-409e-98ab-2a563f37e389_1638856701024.jpg
  92. 38
      backend/utils/utils.py
  93. 1
      frontend/edu-connect/js-cookie.d.ts
  94. 19
      frontend/edu-connect/next.config.mjs
  95. 2150
      frontend/edu-connect/package-lock.json
  96. 27
      frontend/edu-connect/package.json
  97. 113
      frontend/edu-connect/src/app/(admin)/admin/category/_partials/CategoryTable.tsx
  98. 49
      frontend/edu-connect/src/app/(admin)/admin/category/page.tsx
  99. 172
      frontend/edu-connect/src/app/(admin)/admin/course/_partials/CourseTable.tsx
  100. 48
      frontend/edu-connect/src/app/(admin)/admin/course/page.tsx
  101. Some files were not shown because too many files have changed in this diff Show More

1
.gitignore vendored

@ -26,6 +26,7 @@ env/
.venv/
__pypackages__/
**.env
# Node.js
node_modules/

@ -1 +1,53 @@
# HackX Entry By FreeBug
# eduConnect by Team freeBug
Welcome to eduConnect, a revolutionary platform where users can gain and share knowledge in specific domains. eduConnect offers a seamless experience for users to learn, interact, and grow in their areas of interest. Whether you're an expert wanting to share your insights or a learner eager to explore, eduConnect has you covered.
## 🚀 Features
### 🔐 User Authentication
- Register and Login: Secure authentication system for users to create accounts and access their personalized dashboard.
### 📚 Knowledge Sharing
- Create Courses: Share your expertise by creating courses in specific domains.
- Enroll in Courses: Explore and enroll in various courses to enhance your knowledge.
### 💬 Interactive Learning
- Chat Rooms: Each course has its own chat room, enabling students and instructors to interact and discuss.
- Spam Prevention: Robust measures to prevent spam messages in chat rooms, ensuring meaningful conversations.
### 🧠 Personalized Quizzes
- Identical Quizzes: Based on a user’s read history, tailored quizzes are provided to reinforce learning and retention.
### 📊 Progress Tracking
- Activity Log: Users can keep track of their progress, monitor their activities, and evaluate how much they’ve learned.
- Knowledge Metrics: Insights into user performance and course engagement.
### 🎖 Badges & Rewards
- User Badges: Earn badges for achievements to stay motivated and energized while learning.
## 🛠 Tech Stack
- Backend: Python, Flask
- Frontend: HTML, CSS, JavaScript
- Database: PostgreSQL/MySQL
- Authentication: JWT-based secure authentication
- Hosting: AWS/Heroku/Your Hosting Solution
## 📄 Installation Guide
### Prerequisites
1. Python 3.8+ installed on your system.
2. PostgreSQL/MySQL database setup.
3. Node.js and npm (if applicable for frontend).
4. A virtual environment for Python dependencies.
### Steps
1. Clone the Repository
bash
git clone https://hackethon.ai/hack/HackXlbef/FreeBug.git
cd eduConnect

@ -11,14 +11,29 @@ import uuid
from datetime import datetime
from constants import UserRole
from utils.utils import random_string_generator, hash_string
from flask_cors import CORS
from blueprints.profile import profile as profileBlueprint
from blueprints.session import session as sessionBlueprint
from blueprints.admin import admin as adminBlueprint
from blueprints.chat import chat as chatBlueprint
from blueprints.public import public_summary as publicBlueprint
from blueprints.course import course as courseBlueprint
from blueprints.badge import badge_route as badgeBlueprint
from blueprints.quiz import quiz as quizBlueprint
app = Flask(__name__)
# Enable CORS for all routes
CORS(app)
# Enable CORS for specific routes
# CORS(app, resources={
# r"*": {"origins": "*"} # Allows CORS for all `/api/` routes
# })
# Set configuration directly on the app instance
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'}
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif','pdf'}
app.config['MAX_CONTENT_LENGTH'] = 1600 * 1000 * 1000
app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI
@ -26,11 +41,26 @@ db.init_app(app)
app.register_blueprint(profileBlueprint, url_prefix='/api/profile')
app.register_blueprint(sessionBlueprint,url_prefix='/api/session')
app.register_blueprint(adminBlueprint,url_prefix='/api/admin')
app.register_blueprint(badgeBlueprint, url_prefix='/api/badge')
# TODO: Register Notif API
app.register_blueprint(chatBlueprint,url_prefix='/api/chat')
app.register_blueprint(quizBlueprint,url_prefix='/api/quiz')
app.register_blueprint(publicBlueprint,url_prefix='/api/public')
app.register_blueprint(courseBlueprint,url_prefix='/api/course')
@app.route('/media/<string:filename>')
def send_file(filename):
return send_from_directory(USER_UPLOADS_DIR, filename)
@app.route('/courseSegment/<string:filename>/<int:page>/<string:dtype>')
def get_pdf_file_as_pages(filename: str, page: int, dtype: str):
if dtype == 'txt':
return send_from_directory(os.path.join(USER_UPLOADS_DIR, filename+'_parts'), f"{page}.txt")
else:
return send_from_directory(os.path.join(USER_UPLOADS_DIR, filename+'_parts'), f"{page}.pdf")
@app.route('/', methods=['GET', 'POST'])
def homepage():
return {'message': 'Welcome back !'}, 200
@ -53,7 +83,7 @@ def seed_data():
joinedDate=datetime.utcnow(), lastOnline=datetime.utcnow(),
bio=f"This is user{i}'s bio.", role=int(UserRole.USER),
isActivated=True, sessions=[], user_badges=[],
enrollments=[], quizzes=[], quiz_attempts=[], chats=[], notifications=[])
enrollments=[], quizzes=[], quiz_attempts=[], chats=[], notifications=[],publications=[])
for i in range(1, 6) ]
db.session.add_all(users)
db.session.commit()

@ -1,5 +1,5 @@
from utils .auth import auth_required, requires_role
from flask import Blueprint, jsonify, g,url_for
from flask import Blueprint, jsonify, g, url_for
from db.model import User, Course, Enrollment, Chat, db
from sqlalchemy import select, func, desc, and_
from datetime import datetime, timedelta
@ -20,20 +20,19 @@ def get_user_stats():
total_users = db.session.execute(
select(func.count()).select_from(User)
).scalar()
# Get authors (users who have created courses)
authors = db.session.execute(
distinct_authors_count = db.session.execute(
select(func.count(func.distinct(Course.authorID)))
.select_from(Course)
).scalar()
return jsonify({
'stats': {
'totalUsers': total_users,
'totalAuthors': authors
'totalAuthors': distinct_authors_count
}
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@ -54,7 +53,7 @@ def get_enrollment_stats():
)
.select_from(Enrollment)
).first()
# Get course-wise enrollment counts
course_stats = db.session.execute(
select(
@ -76,7 +75,7 @@ def get_enrollment_stats():
} for stat in course_stats]
}
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@ -91,7 +90,7 @@ def get_discussion_stats():
try:
# Get activity for last 24 hours
twenty_four_hours_ago = datetime.now() - timedelta(hours=24)
# Get active rooms and their stats
active_rooms = db.session.execute(
select(
@ -104,10 +103,10 @@ def get_discussion_stats():
.group_by(Course.id)
.order_by(desc('message_count'))
).all()
# Get total active rooms
total_active_rooms = len(active_rooms)
# Get most active room
most_active_room = None
if active_rooms:
@ -117,7 +116,7 @@ def get_discussion_stats():
'messageCount': most_active.message_count,
'activeUsers': most_active.active_users
}
# Get total active users across all rooms
total_active_users = db.session.execute(
select(func.count(func.distinct(Chat.userID)))
@ -136,11 +135,9 @@ def get_discussion_stats():
} for room in active_rooms]
}
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@admin.route('/stats/userDetail', methods=['GET'])
@auth_required()
@ -152,13 +149,13 @@ def get_user_details():
"""
try:
current_user: User = g.current_user
# Get all users with basic info
users = db.session.execute(
select(User)
.order_by(desc(User.joinedDate))
).scalars()
# Format user data
user_list = [{
'id': str(user.id),
@ -174,11 +171,13 @@ def get_user_details():
'lastOnline': user.lastOnline.isoformat(),
'dateOfBirth': user.dob.isoformat() if user.dob else None
} for user in users]
return jsonify({
'users': user_list,
'count': len(user_list)
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500

@ -1,152 +1,48 @@
from flask import Blueprint, request, jsonify, g
from werkzeug.datastructures import MultiDict
import os
import uuid
from flask import Blueprint, url_for, jsonify, g
from utils.auth import auth_required
from db.model import db, Badge, UserBadge
from sqlalchemy import select
from config import DEFAULT_BADGE_ICON, USER_UPLOADS_DIR
from db.model import db, Badge, User
from utils.utils import random_string_generator
from utils.auth import auth_required, requires_role
from constants import UserRole
badge_route = Blueprint('badge', __name__)
badge = Blueprint('badge', __name__)
@badge_route.route('/listAllBadges')
def all_badges():
badges: list[Badge] = db.session.execute(select(Badge)).scalars()
data: list = []
for bgd in badges:
data.append({
'id': bgd.id,
'name': bgd.name,
'description': bgd.description,
'createDate': bgd.createDate,
'icon': url_for('send_file', filename=bgd.icon),
'canClaim': bgd.canClaim
})
return jsonify({
'count': len(data),
'data': data
})
@badge.route('/create', methods=['POST'])
@badge_route.route('/myBadges')
@auth_required()
@requires_role([UserRole.ADMIN])
def create_badge():
"""
Create a new badge. Only accessible by admin users.
Requires: badge_name, description
Optional: icon_image, can_claim
"""
try:
form_data: dict = request.form
badge_uploaded_icon: MultiDict|None = request.files.get('icon_image', None)
# Validate required fields
try:
badge_name: str = form_data['badge_name']
if len(badge_name) > 16:
return jsonify({'message': 'Badge name cannot exceed 16 characters'}), 400
except KeyError:
return jsonify({'message': 'Badge name cannot be empty'}), 400
# Handle icon upload
icon_file_name: str = DEFAULT_BADGE_ICON
if badge_uploaded_icon is not None:
icon_file_name = f"{random_string_generator(32)}.{badge_uploaded_icon.filename.split('.')[-1]}"
badge_uploaded_icon.save(os.path.join(USER_UPLOADS_DIR, icon_file_name))
# Get optional fields
badge_description: str = form_data.get('description', '')
can_claim: bool = form_data.get('can_claim', 'false').lower() == 'true'
# Create new badge
new_badge: Badge = Badge(
name=badge_name,
description=badge_description,
icon=icon_file_name,
canClaim=can_claim,
user_badges=[]
)
db.session.add(new_badge)
db.session.commit()
return jsonify({
'message': 'Badge was created successfully.',
'badge': {
'id': str(new_badge.id),
'name': new_badge.name,
'description': new_badge.description,
'icon': new_badge.icon,
'canClaim': new_badge.canClaim
}
}), 200
except Exception as e:
db.session.rollback()
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@badge.route('/update/<badge_id>', methods=['PUT'])
@auth_required()
@requires_role([UserRole.ADMIN])
def update_badge(badge_id):
"""
Update an existing badge. Only accessible by admin users.
"""
try:
badge_to_update = db.session.get(Badge, uuid.UUID(badge_id))
if not badge_to_update:
return jsonify({'message': 'Badge not found'}), 404
form_data: dict = request.form
badge_uploaded_icon: MultiDict|None = request.files.get('icon_image', None)
# Update icon if provided
if badge_uploaded_icon is not None:
new_icon_name = f"{random_string_generator(32)}.{badge_uploaded_icon.filename.split('.')[-1]}"
badge_uploaded_icon.save(os.path.join(USER_UPLOADS_DIR, new_icon_name))
# Remove old icon if it's not the default
if badge_to_update.icon != DEFAULT_BADGE_ICON:
try:
os.remove(os.path.join(USER_UPLOADS_DIR, badge_to_update.icon))
except OSError:
pass # File might not exist
badge_to_update.icon = new_icon_name
# Update other fields if provided
if 'badge_name' in form_data:
if len(form_data['badge_name']) > 16:
return jsonify({'message': 'Badge name cannot exceed 16 characters'}), 400
badge_to_update.name = form_data['badge_name']
if 'description' in form_data:
badge_to_update.description = form_data['description']
if 'can_claim' in form_data:
badge_to_update.canClaim = form_data['can_claim'].lower() == 'true'
db.session.commit()
return jsonify({
'message': 'Badge was updated successfully.',
'badge': {
'id': str(badge_to_update.id),
'name': badge_to_update.name,
'description': badge_to_update.description,
'icon': badge_to_update.icon,
'canClaim': badge_to_update.canClaim
}
}), 200
except Exception as e:
db.session.rollback()
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@badge.route('/list', methods=['GET'])
@auth_required()
def list_badges():
"""
List all badges. Accessible by all authenticated users.
"""
try:
badges = db.session.execute(
select(Badge)
.order_by(Badge.createDate.desc())
).scalars().all()
return jsonify({
'badges': [{
'id': str(badge.id),
'name': badge.name,
'description': badge.description,
'icon': badge.icon,
'canClaim': badge.canClaim,
'createDate': badge.createDate.isoformat()
} for badge in badges]
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
def my_badges():
user_badges: list[UserBadge] = db.session.execute(select(UserBadge).where(
UserBadge.userID == g.current_user.id
)).scalars()
data: list = []
for ub in user_badges:
bgd = ub.badge
data.append({
'id': ub.id,
'badgeID': bgd.id,
'userID': ub.userID,
'name': bgd.name,
'description': bgd.description,
'createDate': bgd.createDate,
'icon': url_for('send_file', filename=bgd.icon),
'canClaim': bgd.canClaim,
'claimedDate': ub.claimedDate,
})
return jsonify({
'count': len(data),
'data': data
})

@ -1,55 +1,86 @@
from flask import Blueprint, request, jsonify, g
from db.model import User, Enrollment, Chat, db
import uuid
from sqlalchemy import select, and_, desc
from datetime import datetime
from flask import Blueprint, request, jsonify,g
from uuid import UUID
from db.model import db, User, Course, Enrollment,Chat
from utils.auth import auth_required
import requests
from config import SPAM_SCORE_THRESHOLD, AI_SPAM_SERVICES_MICROSERVICE
from sqlalchemy import desc, select, and_
chat = Blueprint('chat', __name__)
@chat.route('/<uuid:course_id>/messages', methods=['GET'])
def get_course_messages(course_id: uuid.UUID):
"""
Fetch chat messages for a specific course with optional pagination.
Only enrolled users can access the messages.
@chat.route("/send", methods=["POST"])
@auth_required()
def create_chat():
current_user: User = g.current_user # Fetch the logged-in user
data = request.form
if not data.get("course_id"):
return jsonify({"error": "Unknown Course"}), 400
if not data.get("message", "").strip():
return jsonify({'message': 'Silently rejected blank message'}), 200
try:
# Check if the user is enrolled in the course
enrollment_record: Enrollment = db.session.execute(
select(Enrollment).where(and_(
Enrollment.courseID == data.get("course_id"),
Enrollment.userID == current_user.id)
)
).scalar()
if not enrollment_record:
return jsonify({"error": "You are not enrolled in this course."}), 403
if enrollment_record.currentPage < enrollment_record.course.pageForCommunity:
return jsonify({
'message': 'You have not met the requirements to enter the chat.'
}), 403
# Create and save the chat message
spam_response = requests.post(AI_SPAM_SERVICES_MICROSERVICE, json={"test_message": data.get("message").strip()})
if spam_response.status_code != 200:
return jsonify({"error": "Failed to check message for spam."}), 500
spam_score = int(spam_response.json().get("spam_score", 0))
if spam_score > SPAM_SCORE_THRESHOLD:
return jsonify({"error": "This message contains suspicious links or is vulnerable."}), 400
new_chat = Chat(
textContent=data.get("message").strip(),
userID=current_user.id,
user=g.current_user,
course=enrollment_record.course,
courseID=data.get("course_id"),
)
db.session.add(new_chat)
db.session.commit()
Query Parameters:
- before: UUID of the message to get older entries
- after: UUID of the message to get newer entries
- limit: Number of messages to return (default: 10)
"""
return jsonify({"message": "Chat sent successfully.", "chat_id": str(new_chat.id)}), 201
except Exception as e:
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
@chat.route("/get", methods=["GET", "POST"])
@auth_required()
def get_messages():
try:
course_id: uuid.UUID = uuid.UUID(request.form.get('course_id'))
current_user: User = g.current_user
limit = int(request.args.get('limit', 10))
before_id = request.args.get('before')
after_id = request.args.get('after')
limit = int(request.form.get('limit', 10))
before_id = request.form.get('before')
after_id = request.form.get('after')
# Verify user's enrollment
enrollment = db.session.execute(
select(Enrollment).where(
and_(
Enrollment.courseID == course_id,
Enrollment.userID == current_user.id
)
and_(Enrollment.courseID == course_id, Enrollment.userID == current_user.id)
)
).scalar()
if not enrollment:
return jsonify({'message': 'You are not enrolled in this course'}), 401
# Base query
return jsonify({"error": "You are not enrolled in this course."}), 403
query = select(Chat).where(Chat.courseID == course_id)
# Handle pagination
if before_id:
try:
reference_message = db.session.execute(
reference_message: Chat = db.session.execute(
select(Chat).where(Chat.id == uuid.UUID(before_id))
).scalar()
if not reference_message:
return jsonify({'message': 'Reference message not found'}), 404
query = query.where(Chat.chatDate < reference_message.chatDate)
query = query.order_by(Chat.chatDate.desc()).where(Chat.chatDate < reference_message.chatDate)
except ValueError:
return jsonify({'message': 'Invalid message ID format'}), 400
elif after_id:
@ -57,92 +88,27 @@ def get_course_messages(course_id: uuid.UUID):
reference_message = db.session.execute(
select(Chat).where(Chat.id == uuid.UUID(after_id))
).scalar()
if not reference_message:
return jsonify({'message': 'Reference message not found'}), 404
query = query.where(Chat.chatDate > reference_message.chatDate)
# For after queries, reverse the order later
query = query.order_by(Chat.chatDate)
query = query.order_by(Chat.chatDate.asc()).where(Chat.chatDate > reference_message.chatDate)
except ValueError:
return jsonify({'message': 'Invalid message ID format'}), 400
else:
# Default ordering
query = query.order_by(desc(Chat.chatDate))
# Apply limit and execute query
query = query.order_by(Chat.chatDate.desc())
query = query.limit(limit)
messages = list(db.session.execute(query).scalars())
# Reverse the order for 'after' queries to maintain consistency
if after_id:
messages.reverse()
# Format messages
messages = db.session.execute(query).scalars()
chat_messages = [{
'id': str(msg.id),
'text': msg.textContent,
'userId': str(msg.userID),
'username': msg.user.username,
'timestamp': msg.chatDate.isoformat()
'timestamp': msg.chatDate.isoformat(),
'isSelf': int(msg.userID == g.current_user.id)
} for msg in messages]
return jsonify({
'messages': chat_messages,
'count': len(chat_messages),
'hasMore': len(chat_messages) == limit
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@chat.route('/<uuid:course_id>/send', methods=['POST'])
def send_message(course_id: uuid.UUID):
"""
Send a new chat message in a course.
Only enrolled users can send messages.
"""
try:
current_user: User = g.current_user
message_text = request.json.get('message')
if not message_text or not message_text.strip():
return jsonify({'message': 'Message content is required'}), 400
# Verify user's enrollment
enrollment = db.session.execute(
select(Enrollment).where(
and_(
Enrollment.courseID == course_id,
Enrollment.userID == current_user.id
)
)
).scalar()
if not enrollment:
return jsonify({'message': 'You are not enrolled in this course'}), 401
# Create new chat message
new_message = Chat(
textContent=message_text,
userID=current_user.id,
courseID=course_id,
chatDate=datetime.now()
)
db.session.add(new_message)
# Update last activity in enrollment
enrollment.lastActivity = datetime.now()
db.session.commit()
return jsonify({
'message': 'Message sent successfully',
'messageId': str(new_message.id),
'timestamp': new_message.chatDate.isoformat()
}), 201
except Exception as e:
db.session.rollback()
return jsonify({'message': f'An error occurred: {str(e)}'}), 500

@ -1,56 +1,569 @@
from flask import Blueprint, request, jsonify, g
from flask import Blueprint, request, jsonify, g, url_for
from sqlalchemy import select, and_, func, distinct, or_
from sqlalchemy.exc import IntegrityError
from werkzeug.datastructures import MultiDict
import os
import uuid
import math
from config import DEFAULT_COURSE_COVER
from db.model import db, Course, Category, User, Chat
from utils.utils import random_string_generator
from db.model import db, Course, Category, User, Chat, Enrollment
from utils.utils import random_string_generator, split_pdf_into_pages_with_text
from utils.auth import auth_required, requires_role
from constants import *
from config import *
from constants import PublishedStatus
from typing import Union
from db.model import UserRole
course = Blueprint('course', __name__)
@course.route('/create', methods=['POST'])
@course.route('/listAll')
def list_all_courses():
limit: int = int(request.args.get('limit', 10))
offset: int = int(request.args.get('offset', 0))
category_uuid: str = request.args.get('category_uuid')
search_q: str = request.args.get('search_q', '').strip()
sort_by: str = request.args.get('sort_by', '').strip()
show_pending: bool = bool(int(request.args.get('show_pending', 0)))
available_sorts = ['date_asc', 'date_desc', 'name_asc', 'name_desc', 'students_desc', 'students_asc']
if category_uuid is not None:
category_uuid: uuid.UUID = uuid.UUID(request.args.get('category_uuid'))
# Build the query as required
query: select = select(Course)
if search_q != '':
query = query.where(or_(Course.name.like(f'%{search_q}%'), Course.description.like(f'%{search_q}%'),
User.firstName.like(f'%{search_q}%')))
if category_uuid is not None:
query = query.where(Course.categoryID == category_uuid)
if g.get('is_authed'):
if show_pending and g.current_user.role == int(UserRole.ADMIN):
query = query.where(Course.publishedStatus == int(PublishedStatus.PENDING))
else:
query = query.where(Course.publishedStatus == int(PublishedStatus.APPROVED))
#total_pages_for_offset: int = db.session.execute(func.count(Course.id).select_from(Course)).scalar()/limit
total_pages_for_offset: int = db.session.execute(
select(func.count()).select_from(query.subquery())
).scalar() / limit
query = query.limit(limit).offset(offset)
total_pages: int = math.ceil(total_pages_for_offset)
if sort_by in available_sorts:
if sort_by == 'date_asc':
query = query.order_by(Course.creationDate.asc())
elif sort_by == 'date_desc':
query = query.order_by(Course.creationDate.desc())
elif sort_by == 'name_asc':
query = query.order_by(Course.name.asc())
elif sort_by == 'name_desc':
query = query.order_by(Course.name.desc())
elif sort_by == 'students_desc':
query = query.order_by(Course.totalEnrolled.desc())
elif sort_by == 'students_asc':
query = query.order_by(Course.totalEnrolled.asc())
courses: list[Course] = db.session.execute(query).scalars()
course_list: list[dict] = []
for item in courses:
course_list.append(
{
'id': item.id,
'name': item.name,
'description': item.description,
'isActive': item.isActive,
'creationDate': item.creationDate,
'coverImage': url_for('send_file', filename=item.coverImage),
'totalEnrolled': item.totalEnrolled,
'pdf0': url_for('send_file', filename=item.serverFilename),
'author': {
'id': item.author.id,
'firstName': item.author.firstName,
'lastName': item.author.lastName,
'username': item.author.username,
'bio': item.author.bio,
'lastOnline': item.author.lastOnline,
'pfpFilename': url_for('send_file', filename=item.author.pfpFilename)
},
'category': {
'id': item.categoryID,
'name': item.category.name,
'description': item.category.description
}
})
return jsonify({
'total_pages': total_pages,
'current_offset': offset,
'limit': limit,
'data': course_list,
})
@course.route('/enroll',methods=['POST'])
@auth_required()
def enroll_user():
if not request.form.get('course_uuid'):
return jsonify({'message': 'Missing required parameter "course_uuid" '}), 400
course_uuid: uuid.UUID = uuid.UUID(request.form.get('course_uuid'))
selected_course: Course = db.session.execute(select(Course).where(Course.id == course_uuid)).scalar()
if not selected_course:
return jsonify({'message': 'Course not found'}), 404
#make sure if user is already enrolled h/she can't enroll
if db.session.execute(select(Enrollment).where(and_(Enrollment.userID == g.current_user.id, Enrollment.courseID == course_uuid))).scalar():
return jsonify({'message': 'Already enrolled to this course'}), 400
new_enroll: Enrollment = Enrollment(
userID=g.current_user.id,
courseID=course_uuid,
user=g.current_user,
course=selected_course
)
try:
selected_course.totalEnrolled = selected_course.totalEnrolled + 1
db.session.add(new_enroll)
db.session.commit()
except IntegrityError:
return jsonify({'message': 'Already enrolled to this course'})
return jsonify({'message': 'Enrollment successful'}), 200
@course.route('/createCourse', methods=['POST'])
@auth_required()
def create_course():
form_data: dict = request.form
course_uploaded_cover_image: MultiDict|None = request.files.get('cover_image', None)
course_uploaded_pdf: MultiDict|None = request.files.get('course_pdf', None)
cover_file_name: str = DEFAULT_COURSE_COVER
pdf_file_name: str = ''
pdf_total_pages: int = 1
if course_uploaded_cover_image is not None:
cover_file_name: str = random_string_generator(32)+course_uploaded_cover_image.filename.split('.')[-1]
print("COURSE COVER ATTACHED")
cover_file_name: str = random_string_generator(32)+"."+course_uploaded_cover_image.filename.split('.')[-1]
course_uploaded_cover_image.save(os.path.join(USER_UPLOADS_DIR, cover_file_name))
if course_uploaded_pdf is not None:
pdf_file_name: str = random_string_generator(32) + course_uploaded_pdf.filename.split('.')[-1]
print('PDF ATTACHED')
pdf_file_name: str = random_string_generator(32) +"."+ course_uploaded_pdf.filename.split('.')[-1]
course_uploaded_pdf.save(os.path.join(USER_UPLOADS_DIR, pdf_file_name))
published_status: PublishedStatus = PublishedStatus.DRAFT
pdf_parts_root_dir = os.path.join(USER_UPLOADS_DIR, pdf_file_name + "_parts")
os.makedirs(pdf_parts_root_dir, exist_ok=True)
pdf_total_pages = split_pdf_into_pages_with_text(
pdf_path=os.path.join(USER_UPLOADS_DIR, pdf_file_name),
output_directory=pdf_parts_root_dir
)
published_status: PublishedStatus = PublishedStatus.PENDING
try:
course_name: str = form_data['course_name']
except KeyError:
return jsonify({'message': 'Course name cannot be empty'}), 401
course_description: str = form_data.get('course_description', '')
category_id: uuid.UUID = uuid.UUID(form_data['category_uuid'])
pages_required_for_community: int = int(form_data['community_unlock_at_pages']) # TODO: Add this field to model
page_for_community: int = int(form_data.get('page_for_community', 1)) # TODO: Add this field to model
category: Category = db.session.execute(select(Category).where(Category.id == category_id)).scalar()
# author: User = db.session.execute(select(User).where(User.id == g.current_user.id)).scalar()
new_course: Course = Course(
name=course_name,
categoryID=category_id,
authorID=g.current_user.id,
category=category,
author=g.current_user,
description=course_description,
isActive=True,
pageForCommunity=page_for_community,
publishedStatus=int(published_status),
coverImage=cover_file_name,
serverFilename =pdf_file_name,
serverFilename=pdf_file_name,
totalPages=pdf_total_pages,
enrollments=[],
quizzes=[],
chats=[]
)
# chat: Chat = Chat(courseID=new_course.id) TODO: Add a welcome chat for this course
db.session.add_all(new_course)
db.session.add(new_course)
db.session.commit()
return jsonify({'message': 'Course was created successfully.'}), 200
return jsonify({'message': 'Course was created successfully.', 'courseID': new_course.id}), 200
@course.route('/update', methods=['UPDATE', 'DELETE'])
@auth_required()
def update_course():
form_data = request.form
course_id: uuid.UUID = uuid.UUID(form_data['course_id'])
selected_course: Course|None = None
if g.current_user.role == int(UserRole.ADMIN):
selected_course: Course = db.session.execute(select(Course).where(and_(
Course.id == course_id
))).scalar()
else:
selected_course: Course = db.session.execute(select(Course).where(and_(
Course.id == course_id, Course.publishedStatus != int(PublishedStatus.BANNED)
))).scalar()
if not selected_course:
return jsonify({'message': 'The course could not be found'}), 404
if request.method == 'DELETE':
if selected_course.authorID == g.current_user.id or g.current_user.role == int(UserRole.ADMIN):
db.session.delete(selected_course)
db.session.commit()
return jsonify({'message': 'Course was deleted successfully'}), 200
else:
return jsonify({'message': 'Unauthorized for this change'}), 401
else:
# Update the data
if selected_course.authorID == g.current_user.id or g.current_user.role == int(UserRole.ADMIN):
if form_data.get('course_name'):
selected_course.name = form_data.get('course_name')
if form_data.get('course_description'):
selected_course.description = form_data.get('course_description')
if form_data.get('category_uuid'):
selected_course.categoryID = uuid.UUID(form_data.get('category_uuid'))
if form_data.get('isActive'):
selected_course.isActive = bool(int(form_data.get('active')))
# Admin Guarded
if form_data.get('published_status'):
if g.current_user.role != int(UserRole.ADMIN):
return jsonify({'message': 'Unauthorized'}), 401
valid_states: list[int] = [
int(e) for e in
[PublishedStatus.APPROVED,
PublishedStatus.PENDING,
PublishedStatus.DECLINED,
PublishedStatus.REVOKED,
PublishedStatus.BANNED,
PublishedStatus.DRAFT]
]
if int(form_data.get('published_status')) not in valid_states:
return jsonify({'message': 'Invalid state to update'}), 401
selected_course.publishedStatus = int(form_data.get('published_status'))
if request.files.get('cover_image'):
cover_file_name: str = random_string_generator(32) + request.files.get('cover_image').filename.split('.')[-1]
request.files.get('cover_image').save(os.path.join(USER_UPLOADS_DIR, cover_file_name))
selected_course.coverImage = cover_file_name
if request.files.get('course_pdf'):
pdf_file_name: str = random_string_generator(32) + request.files.get('course_pdf').filename.split('.')[1]
request.files.get('course_pdf').save(os.path.join(USER_UPLOADS_DIR, pdf_file_name))
selected_course.serverFilename = pdf_file_name
if g.current_user.role != int(UserRole.ADMIN):
selected_course.publishedStatus = int(PublishedStatus.PENDING)
db.session.commit()
return jsonify({'message': 'Course info updated'}), 200
else:
return jsonify({'message': 'Unauthorized for this change'}), 401
@course.route('/info/<string:course_uuid>')
def course_info(course_uuid):
course_uuid: uuid.UUID = uuid.UUID(course_uuid)
selected_course: Course = db.session.execute(select(Course).where(and_(Course.id == course_uuid))).scalar()
if not selected_course:
return jsonify({'message': 'The course does not exist'}), 404
# Only allow owner or admin to query info for course that is not published or is pending
if not selected_course.isActive or selected_course.publishedStatus != int(PublishedStatus.APPROVED):
if g.get("is_authed"):
if g.current_user.role == int(UserRole.ADMIN) or g.current_user.id == selected_course.authorID:
pass
else:
return jsonify({'message': 'The course does not exist.'}), 404
self_enrollment_record: Union[None, Enrollment] = None
self_enrollment_data: dict = {}
if g.get("is_authed"):
self_enrollment_record: Enrollment = db.session.execute(
select(Enrollment).where(
and_(
Enrollment.courseID == selected_course.id, Enrollment.userID == g.current_user.id
)
)
)
if self_enrollment_record:
self_enrollment_data = {
'lastActivity': self_enrollment_record.lastActivity,
'currentPage': self_enrollment_record.currentPage,
'maxPage': self_enrollment_record.maxPage,
'joinedDate': self_enrollment_record.joinedDate,
'userID': self_enrollment_record.userID
}
# Get total enrolled user and total unique user chatting about the course and put it in dict
summary_user: dict = {
'totalEnrolled': db.session.execute(
select(func.count(Enrollment.id)).where(Enrollment.courseID == course_uuid)
).scalar(),
'usersInChat': db.session.execute(
select(func.count(distinct(Chat.userID))).select_from(Chat).where(Chat.courseID == course_uuid)
).scalar(),
'totalChats': db.session.execute(
select(func.count()).select_from(Chat).where(Chat.courseID == course_uuid)
).scalar()
}
pages: list = []
if self_enrollment_record:
for i in range(selected_course.totalPages):
pages.append(
url_for('get_pdf_file_as_pages',
filename=selected_course.serverFilename,
page=i + 1,
dtype='pdf')
)
else:
if selected_course.totalPages < 3:
pages.append(
url_for('get_pdf_file_as_pages',
filename=selected_course.serverFilename,
page=1,
dtype='pdf')
)
else:
for i in range(3):
pages.append(
url_for('get_pdf_file_as_pages',
filename=selected_course.serverFilename,
page=i+1,
dtype='pdf')
)
return jsonify({
'message': 'successful',
'data': {
'id': selected_course.id,
'courseName': selected_course.name,
'courseDescription': selected_course.description,
'isActive': selected_course.isActive,
'publishedStatus': selected_course.publishedStatus,
'creationDate': selected_course.creationDate, # TODO: Format to particular structure
'coverImage': url_for('send_file', filename=selected_course.coverImage),
'serverFilename': url_for('send_file', filename=selected_course.serverFilename),
'totalPages': 100,
'pages': pages,
'author': {
'id': selected_course.authorID,
'username': selected_course.author.username,
'firstName': selected_course.author.firstName,
'lastName': selected_course.author.lastName,
'pfpFilename': url_for('send_file', filename=selected_course.author.pfpFilename),
'bio': selected_course.author.bio
},
'selfEnrollment': {
'isEnrolled': self_enrollment_record is not None,
'data': self_enrollment_data
},
'enrollmentSummary': summary_user
}
}), 200
@course.route('/getCategories', methods=['GET'])
def get_categories():
categories: list[Category] = db.session.execute(select(Category)).scalars()
cat_list: list[dict] = []
for category in categories:
cat_list.append(
{
'id': category.id,
'name': category.name,
'description': category.description,
'isActive': category.isActive,
'creationDate': category.creationDate
}
)
return jsonify(cat_list), 200
@course.route('/createCategory', methods=['Post'])
@auth_required()
@requires_role([UserRole.ADMIN])
def create_category():
try:
new_cat: Category = Category(
name=request.form['name'],
description=request.form.get('description'),
isActive=bool(int(request.form.get('isActive'))),
courses=[]
)
except KeyError:
return jsonify({'message': 'Missing required parameter "name" '}), 400
db.session.add(new_cat)
db.session.commit()
return jsonify({'message': 'Category created'}), 201
@course.route('/updateCategory', methods=['POST', 'DELETE'])
@auth_required()
@requires_role([UserRole.ADMIN])
def update_category():
form_data: dict = request.form
try:
category_id: uuid.UUID = uuid.UUID(form_data['category_id'])
except KeyError:
return jsonify({'message': 'Missing required parameter "category_id" '}), 400
selected_category: Category = db.session.execute(select(Category).where(Category.id == category_id)).scalar()
if not selected_category:
return jsonify({'message': 'Category not found'}), 404
if request.method == 'DELETE':
db.session.delete(selected_category)
db.session.commit()
return jsonify({'message': 'Category deleted'}), 200
else:
if form_data.get('name'):
selected_category.name = form_data.get('name')
if form_data.get('description'):
selected_category.description = form_data.get('description')
if form_data.get('isActive'):
selected_category.isActive = bool(int(form_data.get('isActive')))
db.session.commit()
return jsonify({'message': 'Category updated'}), 200
@course.route('/enrolled')
@auth_required()
def enrolled_courses():
enrollments: Course = db.session.execute(
select(Enrollment).where(
and_(Enrollment.userID == g.current_user.id, Course.publishedStatus == int(PublishedStatus.APPROVED))
)
).scalars()
enrolled_list: list[dict] = []
for enroll_row in enrollments:
item = enroll_row.course
enrolled_list.append(
{
'id': enroll_row.id,
'course_id': enroll_row.courseID,
'lastActivity': enroll_row.lastActivity,
'currentPage': enroll_row.currentPage,
'maxPage': enroll_row.maxPage,
'joinedDate': enroll_row.joinedDate,
'userID': enroll_row.userID,
'name': item.name,
'description': item.description,
'isActive': item.isActive,
'creationDate': item.creationDate,
'coverImage': url_for('send_file', filename=item.coverImage),
'totalEnrolled': item.totalEnrolled,
'author': {
'id': item.author.id,
'firstName': item.author.firstName,
'lastName': item.author.lastName,
'username': item.author.username,
'bio': item.author.bio,
'lastOnline': item.author.lastOnline,
'pfpFilename': url_for('send_file', filename=item.author.pfpFilename)
},
'category': {
'id': item.categoryID,
'name': item.category.name,
'description': item.category.description
}
})
return jsonify(enrolled_list), 200
@course.route('/myCourses')
@auth_required()
def my_courses():
courses: Course = db.session.execute(select(Course).where(Course.authorID == g.current_user.id)
).scalars()
course_list: list[dict] = []
for item in courses:
course_list.append(
{
'id': item.id,
'name': item.name,
'description': item.description,
'isActive': item.isActive,
'publishedStatus': item.publishedStatus,
'creationDate': item.creationDate,
'coverImage': url_for('send_file', filename=item.coverImage),
'totalEnrolled': item.totalEnrolled,
'author': {
'id': item.author.id,
'firstName': item.author.firstName,
'lastName': item.author.lastName,
'username': item.author.username,
'bio': item.author.bio,
'lastOnline': item.author.lastOnline,
'pfpFilename': url_for('send_file', filename=item.author.pfpFilename)
},
'category': {
'id': item.categoryID,
'name': item.category.name,
'description': item.category.description
}
})
return jsonify(course_list), 200
@course.route('/seed', methods=['POST'])
def seed_categories():
"""Seed the database with 10 predefined categories."""
categories = [
{"name": "Programming", "description": "Learn coding and software development.", "isActive": True},
{"name": "History", "description": "Explore historical events and figures.", "isActive": True},
{"name": "Mathematics", "description": "Understand mathematical concepts and theories.", "isActive": True},
{"name": "Science", "description": "Dive into the world of science and discovery.", "isActive": True},
{"name": "Art", "description": "Appreciate and create beautiful works of art.", "isActive": True},
{"name": "Music", "description": "Discover music theory and practice.", "isActive": True},
{"name": "Sports", "description": "Learn about various sports and fitness activities.", "isActive": True},
{"name": "Health", "description": "Focus on wellness and healthy living.", "isActive": True},
{"name": "Technology", "description": "Stay updated with the latest tech trends.", "isActive": True},
{"name": "Business", "description": "Understand business strategies and management.", "isActive": True},
]
# Insert categories into the database
try:
for category in categories:
new_category = Category(
name=category['name'],
description=category['description'],
isActive=category['isActive'],
courses=[]
)
db.session.add(new_category)
db.session.commit()
return jsonify({"message": "Categories seeded successfully."}), 201
except IntegrityError:
db.session.rollback()
return jsonify({"message": "Some categories already exist."}), 400
except Exception as e:
return jsonify({"message": f"An error occurred: {str(e)}"}), 500
@course.route('/seed_courses', methods=['POST'])
def seed_courses():
"""Seed the database with courses for existing categories."""
try:
# Fetch all categories from the database
categories = Category.query.all()
if not categories:
return jsonify({"message": "No categories found. Please seed categories first."}), 400
# Predefined courses with associated category names
courses = [
{"name": "Python Programming", "category": "Programming", "description": "Learn Python from scratch."},
{"name": "World History", "category": "History", "description": "Explore key historical events."},
{"name": "Algebra Basics", "category": "Mathematics", "description": "Understand fundamental algebra concepts."},
{"name": "Physics 101", "category": "Science", "description": "Dive into the principles of physics."},
{"name": "Digital Painting", "category": "Art", "description": "Learn the basics of digital art."},
{"name": "Guitar for Beginners", "category": "Music", "description": "Master the basics of playing guitar."},
{"name": "Fitness Fundamentals", "category": "Sports", "description": "Achieve your fitness goals."},
{"name": "Healthy Living", "category": "Health", "description": "Tips for a healthier lifestyle."},
{"name": "AI Basics", "category": "Technology", "description": "Introduction to Artificial Intelligence."},
{"name": "Business Strategies", "category": "Business", "description": "Learn how to develop business strategies."},
]
# Create and add courses to the database
for course_data in courses:
# Find the category by name
category = next((cat for cat in categories if cat.name == course_data["category"]), None)
if not category:
continue # Skip if the category doesn't exist
# Create a new course
new_course = Course(
name=course_data["name"],
categoryID=category.id,
description=course_data["description"],
authorID=None, # Set an appropriate author ID here
totalPages=10, # Example value
totalEnrolled=0,
isActive=True,
publishedStatus=1,
enrollments=[],
quizzes=[],
chats=[]
)
db.session.add(new_course)
db.session.commit()
return jsonify({"message": "Courses seeded successfully."}), 201
except Exception as e:
db.session.rollback()
return jsonify({"message": f"An error occurred: {str(e)}"}), 500

@ -1,108 +1,3 @@
from flask import Blueprint, jsonify, request, g
from utils.auth import auth_required
from db.model import User, Notification, db
from sqlalchemy import select, and_, desc
from datetime import datetime
import uuid
from flask import Blueprint
notification = Blueprint('notification', __name__)
@notification.route('/get', methods=['GET', 'POST'])
@auth_required()
def handle_notifications():
"""
Unified endpoint for handling notifications:
GET: Fetch notifications with optional before/after pagination
POST: Mark notifications as seen
Query Parameters for GET:
- before: UUID of notification to get older entries
- after: UUID of notification to get newer entries
- limit: Number of notifications to return (default: 10)
POST Body:
{
"notificationIds": ["uuid1", "uuid2", ...]
}
"""
if request.method == 'GET':
try:
current_user: User = g.current_user
limit: int = int(request.args.get('limit', 10))
before_id = request.args.get('before')
after_id = request.args.get('after')
# Base query
query = select(Notification).where(
Notification.userID == current_user.id
)
# Handle pagination
if before_id:
try:
reference_notification = db.session.execute(
select(Notification)
.where(and_(
Notification.id == uuid.UUID(before_id),
Notification.userID == current_user.id
))
).scalar()
if not reference_notification:
return jsonify({'message': 'Reference notification not found'}), 404
query = query.where(
Notification.notifDate < reference_notification.notifDate
)
except ValueError:
return jsonify({'message': 'Invalid notification ID format'}), 400
elif after_id:
try:
reference_notification = db.session.execute(
select(Notification)
.where(and_(
Notification.id == uuid.UUID(after_id),
Notification.userID == current_user.id
))
).scalar()
if not reference_notification:
return jsonify({'message': 'Reference notification not found'}), 404
query = query.where(
Notification.notifDate > reference_notification.notifDate
)
# For after queries, we need to reverse the order later
query = query.order_by(Notification.notifDate)
except ValueError:
return jsonify({'message': 'Invalid notification ID format'}), 400
else:
# Default ordering
query = query.order_by(desc(Notification.notifDate))
# Apply limit and execute query
query = query.limit(limit)
notifications = list(db.session.execute(query).scalars())
# Reverse the order for 'after' queries to maintain consistency
if after_id:
notifications.reverse()
# Format response
notif_list = [{
'id': str(notif.id),
'type': notif.notificationType,
'data': notif.notificationData,
'seen': notif.isSeen,
'date': notif.notifDate.isoformat()
} for notif in notifications]
return jsonify({
'notifications': notif_list,
'count': len(notif_list),
'hasMore': len(notif_list) == limit
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
notification = Blueprint('notification', __name__)

@ -1,5 +1,6 @@
from email.policy import default
from flask import Blueprint, request, jsonify, current_app, g,url_for
from sqlalchemy import select
from werkzeug.utils import secure_filename
from datetime import datetime
from utils.auth import auth_required, requires_role
@ -15,7 +16,6 @@ from sqlalchemy.exc import IntegrityError
# from flask import url_for
profile = Blueprint('profile', __name__)
# Function to check allowed file extensions
def allowed_file(filename):
"""Check if the uploaded file has an allowed extension."""
return '.' in filename and filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS']
@ -103,38 +103,71 @@ def register():
return jsonify({"error": "Registration failed, please try again later."}), 500
#make a get request to get json on hello word
@profile.route('/me', methods=['GET'])
@profile.route('/me', methods=['GET', 'PUT'])
@auth_required()
def get_profile():
def manage_profile():
"""
Fetch the profile details of the currently logged-in user.
Handle GET and PUT requests for the logged-in user's profile.
"""
# Get the current user from the `@auth_required` decorator
current_user: User = g.current_user
try:
profile_url = url_for('send_file', filename=current_user.pfpFilename, _external=True)
except:
profile_url = ""
try:
# Construct the user profile data
profile_data = {
"id": str(current_user.id),
"email": current_user.email,
"first_name": current_user.firstName,
"last_name": current_user.lastName,
"username": current_user.username,
"dob": current_user.dob.isoformat(),
"joined_date": current_user.joinedDate.isoformat(),
"last_online": current_user.lastOnline.isoformat(),
"bio": current_user.bio,
"role": current_user.role,
"pfp_filename": current_user.pfpFilename,
"profile_url":profile_url
}
return jsonify({"profile": profile_data}), 200
except Exception as e:
return jsonify({"error": f"Failed to fetch profile. Error: {str(e)}"}), 500
if g.current_user.role == int(UserRole.ADMIN) and request.form.get('user_id'):
target_user: User = db.session.execute(
select(User).where(User.id == uuid.uuid(request.form.get('user_id')))
).scalar()
if not target_user:
return jsonify({'message': 'User not found'}), 404
else:
current_user = target_user
if request.method == 'GET':
profile_picture = url_for('send_file', filename=current_user.pfpFilename, _external=True)
try:
# Construct the user profile data
profile_data = {
"id": str(current_user.id),
"email": current_user.email,
"firstName": current_user.firstName,
"lastName": current_user.lastName,
"username": current_user.username,
"dob": current_user.dob.isoformat() if current_user.dob else None,
"joined_date": current_user.joinedDate.isoformat(),
"last_online": current_user.lastOnline.isoformat(),
"bio": current_user.bio,
"role": current_user.role,
"pfp_filename": current_user.pfpFilename,
"profile_picture": profile_picture,
}
return jsonify({"profile": profile_data}), 200
except Exception as e:
return jsonify({"error": f"Failed to fetch profile. Error: {str(e)}"}), 500
elif request.method == 'PUT':
# Update the user's profile using form data
try:
first_name = request.form.get('firstName')
last_name = request.form.get('lastName')
username = request.form.get('username')
dob = request.form.get('dob')
bio = request.form.get('bio')
is_activated = request.form.get('isActivated')
if first_name:
current_user.firstName = first_name
if last_name:
current_user.lastName = last_name
if username:
current_user.username = username
if dob:
current_user.dob = dob # Ensure the date format is validated
if bio:
current_user.bio = bio
if is_activated:
current_user.isActivated = bool(int(is_activated))
db.session.commit()
return jsonify({"message": "Profile updated successfully."}), 200
except IntegrityError:
db.session.rollback()
return jsonify({"error": "Username must be unique. Please choose another."}), 400
except Exception as e:
db.session.rollback()
return jsonify({"error": f"Failed to update profile. Error: {str(e)}"}), 500
@profile.route('/update-profile-picture', methods=['PATCH'])
@ -156,22 +189,18 @@ def update_profile_picture():
return jsonify({"error": "No selected file"}), 400
if not allowed_file(file.filename):
return jsonify({"error": "Invalid file type"}), 400
# Secure the filename and save the new file
filename = secure_filename(f"user_{user.id}_{file.filename}")
new_filepath = os.path.join(USER_UPLOADS_DIR, filename)
file.save(new_filepath)
# Delete the old profile picture (if it's not the default)
if user.pfpFilename != DEFAULT_PROFILE_FILE:
old_filepath = os.path.join(USER_UPLOADS_DIR, user.pfpFilename)
if os.path.exists(old_filepath):
os.remove(old_filepath)
# Update the user's profile picture
user.pfpFilename = filename
db.session.commit()
# Generate the new profile URL
profile_url = url_for('send_file',filename=user.pfpFilename,_external=True)
@ -183,4 +212,47 @@ def allowed_file(filename):
Validate file extensions.
"""
allowed_extensions = {'png', 'jpg', 'jpeg', 'gif'}
return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions
return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions
@profile.route('/change-password', methods=['POST'])
@auth_required()
def change_password():
"""
Allow the logged-in user to change their password.
The user must provide the current password, new password, and confirm the new password.
"""
user = g.current_user
data = request.form
if g.current_user.role == int(UserRole.ADMIN) and request.form.get('user_id'):
new_password = data.get('new_password')
target_user: User = db.session.execute(
select(User).where(User.id == uuid.uuid(request.form.get('user_id')))
).scalar()
if not target_user:
return jsonify({'message': 'User not found'}), 404
target_user.hash_password = generate_password_hash(new_password)
db.session.commit()
return jsonify({'message': 'Password changed successfully'}), 200
# Validate input data
current_password = data.get('current_password')
new_password = data.get('new_password')
confirm_password = data.get('confirm_password')
if not current_password or not new_password or not confirm_password:
return jsonify({"error": "All fields (current_password, new_password, confirm_password) are required"}), 400
# Check if current password matches the user's existing password
if not check_password_hash(user.hash_password, current_password):
return jsonify({"error": "Current password is incorrect"}), 400
# Check if new password and confirmation match
if new_password != confirm_password:
return jsonify({"error": "New password and confirm password do not match"}), 400
# Check for password complexity (optional)
# Validate password
try:
password_check_sanity(new_password)
except InsecurePasswordException as e:
return jsonify({"error": str(e)}), 400
# Update the user's password
user.hash_password = generate_password_hash(new_password)
db.session.commit()
return jsonify({"message": "Password updated successfully"}), 200

@ -68,4 +68,4 @@ def get_subscribed_users():
return jsonify({'subscribedNewsletter': subscribed_users}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
return jsonify({'message': f'An error occurred: {str(e)}'}), 500

@ -1,52 +1,228 @@
from flask import Blueprint
from flask import Blueprint, request, jsonify, current_app, g
from db.model import User, Category, Quiz, Course, Enrollment, QuizAttempt
from db.model import db
import json
import os
import uuid
from sqlalchemy import select, and_
import requests
from flask import Blueprint, request, jsonify, g, url_for
from uuid import UUID
from db.model import db, User, Course, Enrollment,Chat, Quiz, QuizAttempt
from utils.auth import auth_required
import requests
from config import SPAM_SCORE_THRESHOLD, AI_SPAM_SERVICES_MICROSERVICE, USER_UPLOADS_DIR, AI_QUIZ_SERVICES_MICROSERVICE
from sqlalchemy import desc, select, and_
quiz = Blueprint('quiz', __name__)
@quiz.route('/create', methods=['POST'])
def create_quiz():
data: dict = request.form
creator_user: User = g.current_user
course_id: uuid.UUID = uuid.UUID(data.get('courseID'))
enrollment_data: Enrollment = db.session.execute(select(Enrollment).where(and_(Enrollment.courseID == course_id, Enrollment.userID == creator_user.id))).scalar()
if not enrollment_data:
return jsonify({'message': 'You are not enrolled in this class'}), 401
get_quiz_data :str = '{"questions":[{"question":"What is the capital of France?","options":["Paris","London","Berlin","Madrid"],"answer":"Paris"},{"question":"What is 2 + 2?","options":["3","4","5","6"],"answer":"4"}]}'
new_quiz = Quiz(
creatorUserID = creator_user.id,
courseID = course_id,
quizJson = get_quiz_data,
quiz_attempts = []
)
db.session.add_all(new_quiz)
@quiz.route('/generate', methods=['POST'])
@auth_required()
def generate_quiz():
try:
course_id: uuid.UUID = uuid.UUID(request.form['course_id'])
current_page: int = int(request.form['page'])
except KeyError:
return jsonify({'message': 'course_id and page must be specified'}), 401
enrollment_record: Enrollment = db.session.execute(
select(Enrollment).where(and_(
Enrollment.courseID == course_id,
Enrollment.userID == g.current_user.id)
)
).scalar()
if not enrollment_record:
return jsonify({"error": "You are not enrolled in this course."}), 403
if current_page > enrollment_record.course.totalPages or current_page < 1:
return jsonify({
'message': 'Page range out of bound. No such page'
}), 404
# Everything is alright, now get the text in current page and generate quiz
current_page_text: str = ''
with open(
os.path.join(
USER_UPLOADS_DIR,
enrollment_record.course.serverFilename+"_parts",
f"{current_page}.txt")
) as f:
current_page_text = f.read()
quiz_data_resp = requests.post(AI_QUIZ_SERVICES_MICROSERVICE, json={"string_message": current_page_text})
if quiz_data_resp.status_code != 200:
return jsonify({"error": "Failed to make quiz request."}), 500
quiz_data = quiz_data_resp.json()
# Insert the quiz into table
rows: list[Quiz] = []
for quiz_item in quiz_data['questions']:
rows.append(
Quiz(
creatorUserID=g.current_user.id,
creatorUser=g.current_user,
quiz_attempts=[],
courseID=course_id,
course=enrollment_record.course,
quizQuestion=quiz_item['question'],
quizAnswers=json.dumps(quiz_item['options']),
quizCorrectAnswer=quiz_item['correct_answer']
)
)
db.session.add_all(rows)
db.session.commit()
return jsonify({'message': 'Course was created successfully.'}), 200
@quiz.route('/attempt', methods=['POST'])
def attempt_quiz():
data: dict = request.form
attempt_user: User = g.current_user
quiz_id: uuid.UUID = uuid.UUID(data.get('quizID'))
course_data: Enrollment = db.session.execute(select(Enrollment).where(and_(Enrollment.courseID == course_data, Enrollment.userID == attempt_user.id))).scalar()
if not course_data:
return jsonify({'message': 'You are not enrolled in this class'}), 401
answerKey: str = '{"questions":[{"question":"What is the capital of France?","options":["Paris","London","Berlin","Madrid"],"answer":"Paris"},{"question":"What is 2 + 2?","options":["3","4","5","6"],"answer":"4"}]}'
#function to calculate Score
score_value = 0
new_attempt = QuizAttempt(
userID = attempt_user,
quizID = quiz_id,
answerKey = answerKey,
score = score_value
return jsonify({'message': 'quizzes were generated for the current page'})
@quiz.route('/get/personalIncomplete')
@auth_required()
def get_incomplete_quiz():
try:
course_id: uuid.UUID = uuid.UUID(request.form['course_id'])
except KeyError:
return jsonify({'message': 'course_id must be specified'}), 401
quiz_rows: list[Quiz] = db.session.execute(select(Quiz).where(
and_(Quiz.creatorUserID == g.current_user.id, Quiz.creatorHasAttempted == False)
)).scalars()
data: list = []
for quiz_row in quiz_rows:
data.append(
{
'id': quiz_row.id,
'isActive': quiz_row.isActive,
'creationDate': quiz_row.creationDate,
'quizAnswers': quiz_row.quizAnswers,
'quizQuestion': quiz_row.quizQuestion,
'course': {
'id': quiz_row.course.id,
'name': quiz_row.course.name,
'description': quiz_row.course.description
},
'creator': {
'id': quiz_row.creatorUserID,
'firstName': quiz_row.creatorUser.firstName,
'lastName': quiz_row.creatorUser.lastName,
'username': quiz_row.creatorUser.username,
'pfpFilename': url_for('send_file', filename=quiz_row.creatorUser.pfpFilename)
}
}
)
return jsonify({
'count': len(data),
'data': data
}), 200
@quiz.route('/get/allComplete')
@auth_required()
def get_complete_quiz():
try:
course_id: uuid.UUID = uuid.UUID(request.form['course_id'])
except KeyError:
return jsonify({'message': 'course_id must be specified'}), 401
quiz_attempts: list[QuizAttempt] = db.session.execute(
select(QuizAttempt).where(and_(
QuizAttempt.userID == g.current_user.id,
Course.id == course_id
))).scalars() # IF THIS DOES NOT WORK, ADD COURSE IF TO QUIZ_ATTEMPT TABLE ITSELF
completes: list = []
for attempt in quiz_attempts:
quiz_row: Quiz = attempt.quiz
completes.append(
{
'id': attempt.id,
'quizID': quiz_row.id,
'isActive': quiz_row.isActive,
'creationDate': quiz_row.creationDate,
'quizAnswers': quiz_row.quizAnswers,
'quizQuestion': quiz_row.quizQuestion,
'userAnswer': attempt.userAnswer,
'quizCorrectAnswer': quiz_row.quizCorrectAnswer,
'isCorrect': attempt.isCorrect,
'course': {
'id': quiz_row.course.id,
'name': quiz_row.course.name,
'description': quiz_row.course.description
},
'creator': {
'id': quiz_row.creatorUserID,
'firstName': quiz_row.creatorUser.firstName,
'lastName': quiz_row.creatorUser.lastName,
'username': quiz_row.creatorUser.username,
'pfpFilename': url_for('send_file', filename=quiz_row.creatorUser.pfpFilename)
}
}
)
return jsonify({
'count': len(completes),
'data': completes
}), 200
@quiz.route('/submit',methods=['POST'])
@auth_required()
def submit_quiz():
try:
answer: str = request.form['answer'].strip()
quiz_id: uuid.UUID = uuid.UUID(request.form['quiz_id'])
except KeyError:
return jsonify({'message': 'course_id and answer must be specified'}), 401
quiz_already_attempted: QuizAttempt = db.session.execute(select(QuizAttempt).where(
and_(QuizAttempt.quizID == quiz_id, QuizAttempt.userID == g.current_user.id )
)).scalar()
if quiz_already_attempted:
return jsonify({'message': 'Already attempted this quiz'}), 401
quiz_row: Quiz = db.session.execute(select(Quiz).where(Quiz.id == quiz_id)).scalar()
if not quiz_row:
return jsonify({'message': 'Quiz does not exist'}), 404
valid_answers: list = json.loads(quiz_row.quizAnswers)
is_correct: bool = False
if answer not in valid_answers:
return jsonify({'message': 'No such choice of answer given'}), 404
if answer == quiz_row.quizCorrectAnswer:
is_correct = True
new_attempt: QuizAttempt = QuizAttempt(
userID=g.current_user.id,
user=g.current_user,
quizID=quiz_id,
quiz=quiz_row,
userAnswer=answer,
isCorrect=int(is_correct)
)
db.session.add_all(new_attempt)
db.session.add(new_attempt)
if quiz_row.creatorUser.id == g.current_user.id:
quiz_row.creatorHasAttempted = True
db.session.commit()
return jsonify({'message': 'Quiz was appended sucessfully'}), 200
return jsonify({
'message': 'Answer submitted',
'isCorrect': is_correct,
'attemptID': new_attempt.id,
'quizID': quiz_row.id,
'quizAnswers': quiz_row.quizAnswers,
'quizQuestion': quiz_row.quizQuestion,
'quizCorrectAnswer': quiz_row.quizCorrectAnswer,
'userAnswer': answer
})
@quiz.route('/quizData')
@auth_required()
def get_quiz_info():
try:
quiz_id: uuid.UUID = uuid.UUID(request.args['quiz_id'])
except KeyError:
return jsonify({'message': 'quiz_id must be specified'}), 401
quiz_row: Quiz = db.session.execute(select(Quiz).where(Quiz.id == quiz_id)).scalar()
if not quiz_row:
return jsonify({'message': 'Quiz does not exist'}), 404
return jsonify({
'id': quiz_row.id,
'isActive': quiz_row.isActive,
'creationDate': quiz_row.creationDate,
'quizAnswers': quiz_row.quizAnswers,
'quizQuestion': quiz_row.quizQuestion,
'course': {
'id': quiz_row.course.id,
'name': quiz_row.course.name,
'description': quiz_row.course.description
},
'creator': {
'id': quiz_row.creatorUserID,
'firstName': quiz_row.creatorUser.firstName,
'lastName': quiz_row.creatorUser.lastName,
'username': quiz_row.creatorUser.username,
'pfpFilename': url_for('send_file', filename=quiz_row.creatorUser.pfpFilename)
}
}), 200

@ -95,3 +95,4 @@ def logout():
target_session.isValid = False
db.session.commit()
return jsonify({'message': 'Session invalidated'}), 200

@ -20,6 +20,10 @@ DISABLE_PASSWORD_SANITY_CHECKS: bool = False
PROJECT_ROOT: os.path = os.path.dirname(os.path.abspath(__file__))
USER_UPLOADS_DIR: str = os.path.abspath(os.path.join(PROJECT_ROOT, "uploads"))
SPAM_SCORE_THRESHOLD: int = 6
AI_SPAM_SERVICES_MICROSERVICE: str ='http://localhost:5000/test-spam'
AI_QUIZ_SERVICES_MICROSERVICE: str = 'http://localhost:5000/generate-questions'
DB_URI: str = f"{DB_ENGINE}://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
ACTIVATE_ACCOUNTS_ON_SIGNUP: bool = True

@ -15,10 +15,14 @@ class PublishedStatus(Enum):
REVOKED = 3
BANNED = 4
DRAFT = 5
def __int__(self):
return self.value
class NotificationTypes(Enum):
MENTION = 0
COURSE_PUBLISH_STATUS_UPDATE = 1
NEW_BADGE = 2
TEXT_WITH_URL = 3
PLAINTEXT_NOTICE = 4
PLAINTEXT_NOTICE = 4
def __int__(self):
return self.value

@ -1,6 +1,6 @@
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, MappedAsDataclass
from sqlalchemy import types, text, String, DateTime, func, Boolean, ForeignKey, SmallInteger
from sqlalchemy import types, text, String, DateTime, func, Boolean, ForeignKey, SmallInteger, Integer
from datetime import datetime
import uuid
from typing import List
@ -73,8 +73,11 @@ class Course(db.Model):
authorID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id"))
author: Mapped["User"] = relationship(back_populates="publications")
description: Mapped[str] = mapped_column(String(1024), nullable=False, default='')
pageForCommunity: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
totalPages: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
totalEnrolled: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
publishedStatus: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=PublishedStatus.DRAFT)
publishedStatus: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=int(PublishedStatus.PENDING))
creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now())
coverImage: Mapped[str] = mapped_column(String(256), nullable=False, default=DEFAULT_COURSE_COVER)
serverFilename: Mapped[str] = mapped_column(String(256), nullable=False, default='')
@ -102,9 +105,12 @@ class Quiz(db.Model):
quiz_attempts: Mapped[List["QuizAttempt"]] = relationship(back_populates="quiz", cascade="all, delete-orphan")
courseID: Mapped[uuid.UUID] = mapped_column(ForeignKey("course.id"))
course: Mapped["Course"] = relationship(back_populates="quizzes")
quizJson: Mapped[str] = mapped_column(String, nullable=False)
quizQuestion: Mapped[str] = mapped_column(String, nullable=False)
quizAnswers: Mapped[str] = mapped_column(String, nullable=False)
quizCorrectAnswer: Mapped[str] = mapped_column(String, nullable=False)
creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now())
isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
creatorHasAttempted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
class QuizAttempt(db.Model):
__tablename__ = 'quiz_attempts'
@ -114,8 +120,8 @@ class QuizAttempt(db.Model):
user: Mapped["User"] = relationship(back_populates="quiz_attempts")
quizID: Mapped[uuid.UUID] = mapped_column(ForeignKey("quiz.id"))
quiz: Mapped["Quiz"] = relationship(back_populates="quiz_attempts")
answerKey: Mapped[str] = mapped_column(String, nullable=False)
score: Mapped[int] = mapped_column(default=0, nullable=False)
userAnswer: Mapped[str] = mapped_column(String, nullable=False)
isCorrect: Mapped[int] = mapped_column(default=0, nullable=False)
attemptDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now())
class Chat(db.Model):

Binary file not shown.

@ -0,0 +1,34 @@
Assessment 3 Instructions INT2024
Discrete Mathematics ICT101 Assessment 3 ( 50%)
Instructions
Assessment Type : Individual Assignment
Purpose of the assessment : To develop a plan for a real -world example of an
application in information technology from the one of the topics given below .
This assessment contributes to the various learning outcomes of your Bachelor
of IT degree.
Assessment Task : In the initial part of assignment, the student will be tested on their skills on
writing literature review of a topic student have learnt in the Discrete Mathematics (ICT101) course
in the week 1 to 6. Students need to read at least 3 articles or bo oks on this topic especially with
application to Information Technology and give detail review of those. Student will also identify one
application of information Technology related to the topic in which he/she is interested and write a
complete account of that interest.
Student can use the following database to find article or books.
o EBSCO Databases
o Emerald Insight
o IBISWorld
o IGI Global
o ProQuest eBooks
o O’Reilly Learnin g
Student will be exploring and analysis the application of information technology related to the topic
which are identified by him/her , and he/she must recognise an application that can be programmed
into computer. Each group must sketch a plane to draw a flow -chart and algorithm. Use some inputs
to test the algorithm (Give different trace table for each input) and identify any problem in the
algorithm. Suggest a plane to rectify or explain why it can ’t be rectified. Each student must write on e
report on its findings.

@ -0,0 +1,45 @@
Assessment 3 Instructions INT2024
Student can choose one from the following Topic. However, after deciding on the topic to work on ,
consult with your tutor.
The topic student group can choose from are :
• Number system used in Computing
• Logic in computing
• Inverse Function in Computing
• Induction Proof and its computing applicatio n
• 16-bit representation
• Cryptography
The written report must have the following sections:
1. Introduction
2. Proper reference of at least three articles or books
3. Write detail review of those articles or books related to the topic student chooses
4. Identify one application in Information Technology in which student is interested.
Write a complete account of that interest
5. Description of why students choose this application
6. Give a complete plane to implement the application into a computer program with use of
flow -chart
7. Write an appropriate algorithm
8. Use at least two inputs to test the algor ithm. Group need to give a trace table for each input.
9. Conclusion
10. Short statement about contributions/Reflections from each group member
11. References
Deadline to submit written report: On or before Sunday 18th May 2024, 11.59pm via Moodle.
The report must be:
1. Word or pdf document (3 to 4 pages long)
2. Size: A4
3. Use Assignment Cover Page (download from Moodle) with your details and signature
4. Single space
5. Font: Calibri, 11pt

@ -0,0 +1,63 @@
Assessment 3 Instructions INT2024
Deduction, Late Submission and Extension
Late submission penalty: - 5% of the total available marks per calendar day unless an extension is
approved. For extension application procedure, please refer to Section 3.3 of the Subject Outline.
Plagiarism
Please read Section 3.4 Plagiarism and Refere ncing, from the Subject Outline. Below is part of the
statement:
“Students plagiarising run the risk of severe penalties ranging from a reduction through to 0 marks for
a first offence for a single assessment task, to exclusion from KOI in the most serio us repeat cases.
Exclusion has serious visa implications.”
“Authorship is also an issue under Plagiarism – KOI expects students to submit their own original work
in both assessment and exams, or the original work of their group in the case of a group pr oject. All
students agree to a statement of authorship when submitting assessments online via Moodle, stating
that the work submitted is their own original work. The following are examples of academic
misconduct and can attract severe penalties:
• Handing in work created by someone else (without acknowledgement), whether copied
from another student, written by someone else, or from any published or electronic
source, is fraud, and falls under the general Plagiarism guidelines.
• Students who willingl y allow another student to copy their work in any assessment may
be considered to assisting in copying/cheating, and similar penalties may be applied. ”
• Any form of AI usage such as ChatGPT in your assessment is considered as plagiarism.
Marking Rubric for Assessment N. 03 (Individual Assignment) ; Value 50%
Criteria Fail
(0 – 49%) Pass
(50 – 64%) Credit
(65 – 74%) Distinction
(75 – 84%) High Distinction
(85 – 100%)
Understanding of the
Topic
4 marks
Inaccurate
mathematical
description
of the Topic Basic
mathematical
description
of the Topic Accurate
mathematical
description
of the Topic Accurate
mathematical
description of
the Topic and
some
connections
with
Information
Technology Polished
mathematical
description
of the Topic
and
references to
Information
Technology

@ -0,0 +1,128 @@
Assessment 3 Instructions INT2024
Evidence of depth of
research with reference
6 marks Little or no
relevant
reading and
references Some
relevant
reading and
references Some
relevant
reading and
references
with
explanations
of
connections
to the Topic Relevant
reading and
references and
clear
connections
illuminating
the Topic Relevant
reading and
refere nces
and polished
connections
illuminating
the Topic
Identifying an application
of Information
Technology relevant to
the topic
2 mark Little or no
connection
between the
topic and the
application Basic
connection
between the
topic and the
application Accurate
application of
the topic to
the
information
technology Accurate and
comprehensive
application of
the topic to
the
information
technology Detail and
complete
account of
application
of the topic
to the
information
technology
Understanding of the
Information technology
application(s)
6 marks Inaccurate
description
of application
of
information
Technology Basic
description
of
application
of
information
Technology Accurate
description
of application
of
information
Technology Accurate
description of
application of
information
Technology
and some
connections
with relevant
topics Polished
description
of
application
of
information
Technology
and
references to
relevant
theories
Detail description of the
choice of the application
7 marks Little or no
evidence is
given for the
choice and
omit. Basic
evidence is
given for the
choice. Accurate
evidence is
given for the
choice. Accurate
evidence is
given for the
choice and
omit with
relevant
analysis Accurate
evidence is
given for the
choice and
omit with
relevant
analysis and
complete
detail

@ -0,0 +1,129 @@
Assessment 3 Instructions INT2024
Design a plane for
computer
implementation
7 marks Plane is not
designed in a
proper
manner Plane is
designed in a
proper
manner and
no flow -chart
is given Plane is
designed in a
proper
manner and
flow -chart is
also given Accurate and
comprehensive
plane is given
with a correct
flow -chart Appropriate
plane with
correct flow -
chart and
complete
detail is
given.
Writing an algorithm
7 marks Inaccurate
algorithm or
algorithm is
given in an
inappropriate
manner Correct
algorithm but
written in an
inappropriate
manner Correct
algorithm
which is
written in an
appropriate
manner Correct
algorithm
which is
written in an
inappropriate
manner with
little discussion Correct
algorithm
which is
written in an
inappropriate
manner with
complete
discussion
Conclusions
7 mark s Little or no
evidence of
accuracy of
the algorithm Basic
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of the
algorithm Complete
analysis and
justification
of accuracy
of the
algorithm
Presentation
1 mark Poorly
organised
report with
unclear
structure Well
organised
report but
with some
errors Clearly
organised
report with
few errors Clearly
organised
report and
good use of
tables and
graphs Polished
report and
creative use
of tables and
graphs
Presentation
5 marks Poor quality
of slides or
poor
performance
of
presentation Well
prepared
presentation
but some
error Well
prepared
presentation
but one or
two error Well prepared
presentation
without an
error and
explain
everything
correctly Well
prepared
presentation
without an
error and
explain
everything
explicitly
with quality.

@ -0,0 +1,15 @@
Assessment 3 Instructions INT2024
Total Mark: / 25
25%
COMMENTS:

@ -0,0 +1,34 @@
Assessment 3 Instructions INT2024
Discrete Mathematics ICT101 Assessment 3 ( 50%)
Instructions
Assessment Type : Individual Assignment
Purpose of the assessment : To develop a plan for a real -world example of an
application in information technology from the one of the topics given below .
This assessment contributes to the various learning outcomes of your Bachelor
of IT degree.
Assessment Task : In the initial part of assignment, the student will be tested on their skills on
writing literature review of a topic student have learnt in the Discrete Mathematics (ICT101) course
in the week 1 to 6. Students need to read at least 3 articles or bo oks on this topic especially with
application to Information Technology and give detail review of those. Student will also identify one
application of information Technology related to the topic in which he/she is interested and write a
complete account of that interest.
Student can use the following database to find article or books.
o EBSCO Databases
o Emerald Insight
o IBISWorld
o IGI Global
o ProQuest eBooks
o O’Reilly Learnin g
Student will be exploring and analysis the application of information technology related to the topic
which are identified by him/her , and he/she must recognise an application that can be programmed
into computer. Each group must sketch a plane to draw a flow -chart and algorithm. Use some inputs
to test the algorithm (Give different trace table for each input) and identify any problem in the
algorithm. Suggest a plane to rectify or explain why it can ’t be rectified. Each student must write on e
report on its findings.

@ -0,0 +1,45 @@
Assessment 3 Instructions INT2024
Student can choose one from the following Topic. However, after deciding on the topic to work on ,
consult with your tutor.
The topic student group can choose from are :
• Number system used in Computing
• Logic in computing
• Inverse Function in Computing
• Induction Proof and its computing applicatio n
• 16-bit representation
• Cryptography
The written report must have the following sections:
1. Introduction
2. Proper reference of at least three articles or books
3. Write detail review of those articles or books related to the topic student chooses
4. Identify one application in Information Technology in which student is interested.
Write a complete account of that interest
5. Description of why students choose this application
6. Give a complete plane to implement the application into a computer program with use of
flow -chart
7. Write an appropriate algorithm
8. Use at least two inputs to test the algor ithm. Group need to give a trace table for each input.
9. Conclusion
10. Short statement about contributions/Reflections from each group member
11. References
Deadline to submit written report: On or before Sunday 18th May 2024, 11.59pm via Moodle.
The report must be:
1. Word or pdf document (3 to 4 pages long)
2. Size: A4
3. Use Assignment Cover Page (download from Moodle) with your details and signature
4. Single space
5. Font: Calibri, 11pt

@ -0,0 +1,63 @@
Assessment 3 Instructions INT2024
Deduction, Late Submission and Extension
Late submission penalty: - 5% of the total available marks per calendar day unless an extension is
approved. For extension application procedure, please refer to Section 3.3 of the Subject Outline.
Plagiarism
Please read Section 3.4 Plagiarism and Refere ncing, from the Subject Outline. Below is part of the
statement:
“Students plagiarising run the risk of severe penalties ranging from a reduction through to 0 marks for
a first offence for a single assessment task, to exclusion from KOI in the most serio us repeat cases.
Exclusion has serious visa implications.”
“Authorship is also an issue under Plagiarism – KOI expects students to submit their own original work
in both assessment and exams, or the original work of their group in the case of a group pr oject. All
students agree to a statement of authorship when submitting assessments online via Moodle, stating
that the work submitted is their own original work. The following are examples of academic
misconduct and can attract severe penalties:
• Handing in work created by someone else (without acknowledgement), whether copied
from another student, written by someone else, or from any published or electronic
source, is fraud, and falls under the general Plagiarism guidelines.
• Students who willingl y allow another student to copy their work in any assessment may
be considered to assisting in copying/cheating, and similar penalties may be applied. ”
• Any form of AI usage such as ChatGPT in your assessment is considered as plagiarism.
Marking Rubric for Assessment N. 03 (Individual Assignment) ; Value 50%
Criteria Fail
(0 – 49%) Pass
(50 – 64%) Credit
(65 – 74%) Distinction
(75 – 84%) High Distinction
(85 – 100%)
Understanding of the
Topic
4 marks
Inaccurate
mathematical
description
of the Topic Basic
mathematical
description
of the Topic Accurate
mathematical
description
of the Topic Accurate
mathematical
description of
the Topic and
some
connections
with
Information
Technology Polished
mathematical
description
of the Topic
and
references to
Information
Technology

@ -0,0 +1,128 @@
Assessment 3 Instructions INT2024
Evidence of depth of
research with reference
6 marks Little or no
relevant
reading and
references Some
relevant
reading and
references Some
relevant
reading and
references
with
explanations
of
connections
to the Topic Relevant
reading and
references and
clear
connections
illuminating
the Topic Relevant
reading and
refere nces
and polished
connections
illuminating
the Topic
Identifying an application
of Information
Technology relevant to
the topic
2 mark Little or no
connection
between the
topic and the
application Basic
connection
between the
topic and the
application Accurate
application of
the topic to
the
information
technology Accurate and
comprehensive
application of
the topic to
the
information
technology Detail and
complete
account of
application
of the topic
to the
information
technology
Understanding of the
Information technology
application(s)
6 marks Inaccurate
description
of application
of
information
Technology Basic
description
of
application
of
information
Technology Accurate
description
of application
of
information
Technology Accurate
description of
application of
information
Technology
and some
connections
with relevant
topics Polished
description
of
application
of
information
Technology
and
references to
relevant
theories
Detail description of the
choice of the application
7 marks Little or no
evidence is
given for the
choice and
omit. Basic
evidence is
given for the
choice. Accurate
evidence is
given for the
choice. Accurate
evidence is
given for the
choice and
omit with
relevant
analysis Accurate
evidence is
given for the
choice and
omit with
relevant
analysis and
complete
detail

@ -0,0 +1,129 @@
Assessment 3 Instructions INT2024
Design a plane for
computer
implementation
7 marks Plane is not
designed in a
proper
manner Plane is
designed in a
proper
manner and
no flow -chart
is given Plane is
designed in a
proper
manner and
flow -chart is
also given Accurate and
comprehensive
plane is given
with a correct
flow -chart Appropriate
plane with
correct flow -
chart and
complete
detail is
given.
Writing an algorithm
7 marks Inaccurate
algorithm or
algorithm is
given in an
inappropriate
manner Correct
algorithm but
written in an
inappropriate
manner Correct
algorithm
which is
written in an
appropriate
manner Correct
algorithm
which is
written in an
inappropriate
manner with
little discussion Correct
algorithm
which is
written in an
inappropriate
manner with
complete
discussion
Conclusions
7 mark s Little or no
evidence of
accuracy of
the algorithm Basic
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of the
algorithm Complete
analysis and
justification
of accuracy
of the
algorithm
Presentation
1 mark Poorly
organised
report with
unclear
structure Well
organised
report but
with some
errors Clearly
organised
report with
few errors Clearly
organised
report and
good use of
tables and
graphs Polished
report and
creative use
of tables and
graphs
Presentation
5 marks Poor quality
of slides or
poor
performance
of
presentation Well
prepared
presentation
but some
error Well
prepared
presentation
but one or
two error Well prepared
presentation
without an
error and
explain
everything
correctly Well
prepared
presentation
without an
error and
explain
everything
explicitly
with quality.

@ -0,0 +1,15 @@
Assessment 3 Instructions INT2024
Total Mark: / 25
25%
COMMENTS:

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@ -0,0 +1,34 @@
Assessment 3 Instructions INT2024
Discrete Mathematics ICT101 Assessment 3 ( 50%)
Instructions
Assessment Type : Individual Assignment
Purpose of the assessment : To develop a plan for a real -world example of an
application in information technology from the one of the topics given below .
This assessment contributes to the various learning outcomes of your Bachelor
of IT degree.
Assessment Task : In the initial part of assignment, the student will be tested on their skills on
writing literature review of a topic student have learnt in the Discrete Mathematics (ICT101) course
in the week 1 to 6. Students need to read at least 3 articles or bo oks on this topic especially with
application to Information Technology and give detail review of those. Student will also identify one
application of information Technology related to the topic in which he/she is interested and write a
complete account of that interest.
Student can use the following database to find article or books.
o EBSCO Databases
o Emerald Insight
o IBISWorld
o IGI Global
o ProQuest eBooks
o O’Reilly Learnin g
Student will be exploring and analysis the application of information technology related to the topic
which are identified by him/her , and he/she must recognise an application that can be programmed
into computer. Each group must sketch a plane to draw a flow -chart and algorithm. Use some inputs
to test the algorithm (Give different trace table for each input) and identify any problem in the
algorithm. Suggest a plane to rectify or explain why it can ’t be rectified. Each student must write on e
report on its findings.

@ -0,0 +1,45 @@
Assessment 3 Instructions INT2024
Student can choose one from the following Topic. However, after deciding on the topic to work on ,
consult with your tutor.
The topic student group can choose from are :
• Number system used in Computing
• Logic in computing
• Inverse Function in Computing
• Induction Proof and its computing applicatio n
• 16-bit representation
• Cryptography
The written report must have the following sections:
1. Introduction
2. Proper reference of at least three articles or books
3. Write detail review of those articles or books related to the topic student chooses
4. Identify one application in Information Technology in which student is interested.
Write a complete account of that interest
5. Description of why students choose this application
6. Give a complete plane to implement the application into a computer program with use of
flow -chart
7. Write an appropriate algorithm
8. Use at least two inputs to test the algor ithm. Group need to give a trace table for each input.
9. Conclusion
10. Short statement about contributions/Reflections from each group member
11. References
Deadline to submit written report: On or before Sunday 18th May 2024, 11.59pm via Moodle.
The report must be:
1. Word or pdf document (3 to 4 pages long)
2. Size: A4
3. Use Assignment Cover Page (download from Moodle) with your details and signature
4. Single space
5. Font: Calibri, 11pt

@ -0,0 +1,63 @@
Assessment 3 Instructions INT2024
Deduction, Late Submission and Extension
Late submission penalty: - 5% of the total available marks per calendar day unless an extension is
approved. For extension application procedure, please refer to Section 3.3 of the Subject Outline.
Plagiarism
Please read Section 3.4 Plagiarism and Refere ncing, from the Subject Outline. Below is part of the
statement:
“Students plagiarising run the risk of severe penalties ranging from a reduction through to 0 marks for
a first offence for a single assessment task, to exclusion from KOI in the most serio us repeat cases.
Exclusion has serious visa implications.”
“Authorship is also an issue under Plagiarism – KOI expects students to submit their own original work
in both assessment and exams, or the original work of their group in the case of a group pr oject. All
students agree to a statement of authorship when submitting assessments online via Moodle, stating
that the work submitted is their own original work. The following are examples of academic
misconduct and can attract severe penalties:
• Handing in work created by someone else (without acknowledgement), whether copied
from another student, written by someone else, or from any published or electronic
source, is fraud, and falls under the general Plagiarism guidelines.
• Students who willingl y allow another student to copy their work in any assessment may
be considered to assisting in copying/cheating, and similar penalties may be applied. ”
• Any form of AI usage such as ChatGPT in your assessment is considered as plagiarism.
Marking Rubric for Assessment N. 03 (Individual Assignment) ; Value 50%
Criteria Fail
(0 – 49%) Pass
(50 – 64%) Credit
(65 – 74%) Distinction
(75 – 84%) High Distinction
(85 – 100%)
Understanding of the
Topic
4 marks
Inaccurate
mathematical
description
of the Topic Basic
mathematical
description
of the Topic Accurate
mathematical
description
of the Topic Accurate
mathematical
description of
the Topic and
some
connections
with
Information
Technology Polished
mathematical
description
of the Topic
and
references to
Information
Technology

@ -0,0 +1,128 @@
Assessment 3 Instructions INT2024
Evidence of depth of
research with reference
6 marks Little or no
relevant
reading and
references Some
relevant
reading and
references Some
relevant
reading and
references
with
explanations
of
connections
to the Topic Relevant
reading and
references and
clear
connections
illuminating
the Topic Relevant
reading and
refere nces
and polished
connections
illuminating
the Topic
Identifying an application
of Information
Technology relevant to
the topic
2 mark Little or no
connection
between the
topic and the
application Basic
connection
between the
topic and the
application Accurate
application of
the topic to
the
information
technology Accurate and
comprehensive
application of
the topic to
the
information
technology Detail and
complete
account of
application
of the topic
to the
information
technology
Understanding of the
Information technology
application(s)
6 marks Inaccurate
description
of application
of
information
Technology Basic
description
of
application
of
information
Technology Accurate
description
of application
of
information
Technology Accurate
description of
application of
information
Technology
and some
connections
with relevant
topics Polished
description
of
application
of
information
Technology
and
references to
relevant
theories
Detail description of the
choice of the application
7 marks Little or no
evidence is
given for the
choice and
omit. Basic
evidence is
given for the
choice. Accurate
evidence is
given for the
choice. Accurate
evidence is
given for the
choice and
omit with
relevant
analysis Accurate
evidence is
given for the
choice and
omit with
relevant
analysis and
complete
detail

@ -0,0 +1,129 @@
Assessment 3 Instructions INT2024
Design a plane for
computer
implementation
7 marks Plane is not
designed in a
proper
manner Plane is
designed in a
proper
manner and
no flow -chart
is given Plane is
designed in a
proper
manner and
flow -chart is
also given Accurate and
comprehensive
plane is given
with a correct
flow -chart Appropriate
plane with
correct flow -
chart and
complete
detail is
given.
Writing an algorithm
7 marks Inaccurate
algorithm or
algorithm is
given in an
inappropriate
manner Correct
algorithm but
written in an
inappropriate
manner Correct
algorithm
which is
written in an
appropriate
manner Correct
algorithm
which is
written in an
inappropriate
manner with
little discussion Correct
algorithm
which is
written in an
inappropriate
manner with
complete
discussion
Conclusions
7 mark s Little or no
evidence of
accuracy of
the algorithm Basic
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of the
algorithm Complete
analysis and
justification
of accuracy
of the
algorithm
Presentation
1 mark Poorly
organised
report with
unclear
structure Well
organised
report but
with some
errors Clearly
organised
report with
few errors Clearly
organised
report and
good use of
tables and
graphs Polished
report and
creative use
of tables and
graphs
Presentation
5 marks Poor quality
of slides or
poor
performance
of
presentation Well
prepared
presentation
but some
error Well
prepared
presentation
but one or
two error Well prepared
presentation
without an
error and
explain
everything
correctly Well
prepared
presentation
without an
error and
explain
everything
explicitly
with quality.

@ -0,0 +1,15 @@
Assessment 3 Instructions INT2024
Total Mark: / 25
25%
COMMENTS:

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

@ -0,0 +1,34 @@
Assessment 3 Instructions INT2024
Discrete Mathematics ICT101 Assessment 3 ( 50%)
Instructions
Assessment Type : Individual Assignment
Purpose of the assessment : To develop a plan for a real -world example of an
application in information technology from the one of the topics given below .
This assessment contributes to the various learning outcomes of your Bachelor
of IT degree.
Assessment Task : In the initial part of assignment, the student will be tested on their skills on
writing literature review of a topic student have learnt in the Discrete Mathematics (ICT101) course
in the week 1 to 6. Students need to read at least 3 articles or bo oks on this topic especially with
application to Information Technology and give detail review of those. Student will also identify one
application of information Technology related to the topic in which he/she is interested and write a
complete account of that interest.
Student can use the following database to find article or books.
o EBSCO Databases
o Emerald Insight
o IBISWorld
o IGI Global
o ProQuest eBooks
o O’Reilly Learnin g
Student will be exploring and analysis the application of information technology related to the topic
which are identified by him/her , and he/she must recognise an application that can be programmed
into computer. Each group must sketch a plane to draw a flow -chart and algorithm. Use some inputs
to test the algorithm (Give different trace table for each input) and identify any problem in the
algorithm. Suggest a plane to rectify or explain why it can ’t be rectified. Each student must write on e
report on its findings.

@ -0,0 +1,45 @@
Assessment 3 Instructions INT2024
Student can choose one from the following Topic. However, after deciding on the topic to work on ,
consult with your tutor.
The topic student group can choose from are :
• Number system used in Computing
• Logic in computing
• Inverse Function in Computing
• Induction Proof and its computing applicatio n
• 16-bit representation
• Cryptography
The written report must have the following sections:
1. Introduction
2. Proper reference of at least three articles or books
3. Write detail review of those articles or books related to the topic student chooses
4. Identify one application in Information Technology in which student is interested.
Write a complete account of that interest
5. Description of why students choose this application
6. Give a complete plane to implement the application into a computer program with use of
flow -chart
7. Write an appropriate algorithm
8. Use at least two inputs to test the algor ithm. Group need to give a trace table for each input.
9. Conclusion
10. Short statement about contributions/Reflections from each group member
11. References
Deadline to submit written report: On or before Sunday 18th May 2024, 11.59pm via Moodle.
The report must be:
1. Word or pdf document (3 to 4 pages long)
2. Size: A4
3. Use Assignment Cover Page (download from Moodle) with your details and signature
4. Single space
5. Font: Calibri, 11pt

@ -0,0 +1,63 @@
Assessment 3 Instructions INT2024
Deduction, Late Submission and Extension
Late submission penalty: - 5% of the total available marks per calendar day unless an extension is
approved. For extension application procedure, please refer to Section 3.3 of the Subject Outline.
Plagiarism
Please read Section 3.4 Plagiarism and Refere ncing, from the Subject Outline. Below is part of the
statement:
“Students plagiarising run the risk of severe penalties ranging from a reduction through to 0 marks for
a first offence for a single assessment task, to exclusion from KOI in the most serio us repeat cases.
Exclusion has serious visa implications.”
“Authorship is also an issue under Plagiarism – KOI expects students to submit their own original work
in both assessment and exams, or the original work of their group in the case of a group pr oject. All
students agree to a statement of authorship when submitting assessments online via Moodle, stating
that the work submitted is their own original work. The following are examples of academic
misconduct and can attract severe penalties:
• Handing in work created by someone else (without acknowledgement), whether copied
from another student, written by someone else, or from any published or electronic
source, is fraud, and falls under the general Plagiarism guidelines.
• Students who willingl y allow another student to copy their work in any assessment may
be considered to assisting in copying/cheating, and similar penalties may be applied. ”
• Any form of AI usage such as ChatGPT in your assessment is considered as plagiarism.
Marking Rubric for Assessment N. 03 (Individual Assignment) ; Value 50%
Criteria Fail
(0 – 49%) Pass
(50 – 64%) Credit
(65 – 74%) Distinction
(75 – 84%) High Distinction
(85 – 100%)
Understanding of the
Topic
4 marks
Inaccurate
mathematical
description
of the Topic Basic
mathematical
description
of the Topic Accurate
mathematical
description
of the Topic Accurate
mathematical
description of
the Topic and
some
connections
with
Information
Technology Polished
mathematical
description
of the Topic
and
references to
Information
Technology

@ -0,0 +1,128 @@
Assessment 3 Instructions INT2024
Evidence of depth of
research with reference
6 marks Little or no
relevant
reading and
references Some
relevant
reading and
references Some
relevant
reading and
references
with
explanations
of
connections
to the Topic Relevant
reading and
references and
clear
connections
illuminating
the Topic Relevant
reading and
refere nces
and polished
connections
illuminating
the Topic
Identifying an application
of Information
Technology relevant to
the topic
2 mark Little or no
connection
between the
topic and the
application Basic
connection
between the
topic and the
application Accurate
application of
the topic to
the
information
technology Accurate and
comprehensive
application of
the topic to
the
information
technology Detail and
complete
account of
application
of the topic
to the
information
technology
Understanding of the
Information technology
application(s)
6 marks Inaccurate
description
of application
of
information
Technology Basic
description
of
application
of
information
Technology Accurate
description
of application
of
information
Technology Accurate
description of
application of
information
Technology
and some
connections
with relevant
topics Polished
description
of
application
of
information
Technology
and
references to
relevant
theories
Detail description of the
choice of the application
7 marks Little or no
evidence is
given for the
choice and
omit. Basic
evidence is
given for the
choice. Accurate
evidence is
given for the
choice. Accurate
evidence is
given for the
choice and
omit with
relevant
analysis Accurate
evidence is
given for the
choice and
omit with
relevant
analysis and
complete
detail

@ -0,0 +1,129 @@
Assessment 3 Instructions INT2024
Design a plane for
computer
implementation
7 marks Plane is not
designed in a
proper
manner Plane is
designed in a
proper
manner and
no flow -chart
is given Plane is
designed in a
proper
manner and
flow -chart is
also given Accurate and
comprehensive
plane is given
with a correct
flow -chart Appropriate
plane with
correct flow -
chart and
complete
detail is
given.
Writing an algorithm
7 marks Inaccurate
algorithm or
algorithm is
given in an
inappropriate
manner Correct
algorithm but
written in an
inappropriate
manner Correct
algorithm
which is
written in an
appropriate
manner Correct
algorithm
which is
written in an
inappropriate
manner with
little discussion Correct
algorithm
which is
written in an
inappropriate
manner with
complete
discussion
Conclusions
7 mark s Little or no
evidence of
accuracy of
the algorithm Basic
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of the
algorithm Complete
analysis and
justification
of accuracy
of the
algorithm
Presentation
1 mark Poorly
organised
report with
unclear
structure Well
organised
report but
with some
errors Clearly
organised
report with
few errors Clearly
organised
report and
good use of
tables and
graphs Polished
report and
creative use
of tables and
graphs
Presentation
5 marks Poor quality
of slides or
poor
performance
of
presentation Well
prepared
presentation
but some
error Well
prepared
presentation
but one or
two error Well prepared
presentation
without an
error and
explain
everything
correctly Well
prepared
presentation
without an
error and
explain
everything
explicitly
with quality.

@ -0,0 +1,15 @@
Assessment 3 Instructions INT2024
Total Mark: / 25
25%
COMMENTS:

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

@ -0,0 +1,34 @@
Assessment 3 Instructions INT2024
Discrete Mathematics ICT101 Assessment 3 ( 50%)
Instructions
Assessment Type : Individual Assignment
Purpose of the assessment : To develop a plan for a real -world example of an
application in information technology from the one of the topics given below .
This assessment contributes to the various learning outcomes of your Bachelor
of IT degree.
Assessment Task : In the initial part of assignment, the student will be tested on their skills on
writing literature review of a topic student have learnt in the Discrete Mathematics (ICT101) course
in the week 1 to 6. Students need to read at least 3 articles or bo oks on this topic especially with
application to Information Technology and give detail review of those. Student will also identify one
application of information Technology related to the topic in which he/she is interested and write a
complete account of that interest.
Student can use the following database to find article or books.
o EBSCO Databases
o Emerald Insight
o IBISWorld
o IGI Global
o ProQuest eBooks
o O’Reilly Learnin g
Student will be exploring and analysis the application of information technology related to the topic
which are identified by him/her , and he/she must recognise an application that can be programmed
into computer. Each group must sketch a plane to draw a flow -chart and algorithm. Use some inputs
to test the algorithm (Give different trace table for each input) and identify any problem in the
algorithm. Suggest a plane to rectify or explain why it can ’t be rectified. Each student must write on e
report on its findings.

@ -0,0 +1,45 @@
Assessment 3 Instructions INT2024
Student can choose one from the following Topic. However, after deciding on the topic to work on ,
consult with your tutor.
The topic student group can choose from are :
• Number system used in Computing
• Logic in computing
• Inverse Function in Computing
• Induction Proof and its computing applicatio n
• 16-bit representation
• Cryptography
The written report must have the following sections:
1. Introduction
2. Proper reference of at least three articles or books
3. Write detail review of those articles or books related to the topic student chooses
4. Identify one application in Information Technology in which student is interested.
Write a complete account of that interest
5. Description of why students choose this application
6. Give a complete plane to implement the application into a computer program with use of
flow -chart
7. Write an appropriate algorithm
8. Use at least two inputs to test the algor ithm. Group need to give a trace table for each input.
9. Conclusion
10. Short statement about contributions/Reflections from each group member
11. References
Deadline to submit written report: On or before Sunday 18th May 2024, 11.59pm via Moodle.
The report must be:
1. Word or pdf document (3 to 4 pages long)
2. Size: A4
3. Use Assignment Cover Page (download from Moodle) with your details and signature
4. Single space
5. Font: Calibri, 11pt

@ -0,0 +1,63 @@
Assessment 3 Instructions INT2024
Deduction, Late Submission and Extension
Late submission penalty: - 5% of the total available marks per calendar day unless an extension is
approved. For extension application procedure, please refer to Section 3.3 of the Subject Outline.
Plagiarism
Please read Section 3.4 Plagiarism and Refere ncing, from the Subject Outline. Below is part of the
statement:
“Students plagiarising run the risk of severe penalties ranging from a reduction through to 0 marks for
a first offence for a single assessment task, to exclusion from KOI in the most serio us repeat cases.
Exclusion has serious visa implications.”
“Authorship is also an issue under Plagiarism – KOI expects students to submit their own original work
in both assessment and exams, or the original work of their group in the case of a group pr oject. All
students agree to a statement of authorship when submitting assessments online via Moodle, stating
that the work submitted is their own original work. The following are examples of academic
misconduct and can attract severe penalties:
• Handing in work created by someone else (without acknowledgement), whether copied
from another student, written by someone else, or from any published or electronic
source, is fraud, and falls under the general Plagiarism guidelines.
• Students who willingl y allow another student to copy their work in any assessment may
be considered to assisting in copying/cheating, and similar penalties may be applied. ”
• Any form of AI usage such as ChatGPT in your assessment is considered as plagiarism.
Marking Rubric for Assessment N. 03 (Individual Assignment) ; Value 50%
Criteria Fail
(0 – 49%) Pass
(50 – 64%) Credit
(65 – 74%) Distinction
(75 – 84%) High Distinction
(85 – 100%)
Understanding of the
Topic
4 marks
Inaccurate
mathematical
description
of the Topic Basic
mathematical
description
of the Topic Accurate
mathematical
description
of the Topic Accurate
mathematical
description of
the Topic and
some
connections
with
Information
Technology Polished
mathematical
description
of the Topic
and
references to
Information
Technology

@ -0,0 +1,128 @@
Assessment 3 Instructions INT2024
Evidence of depth of
research with reference
6 marks Little or no
relevant
reading and
references Some
relevant
reading and
references Some
relevant
reading and
references
with
explanations
of
connections
to the Topic Relevant
reading and
references and
clear
connections
illuminating
the Topic Relevant
reading and
refere nces
and polished
connections
illuminating
the Topic
Identifying an application
of Information
Technology relevant to
the topic
2 mark Little or no
connection
between the
topic and the
application Basic
connection
between the
topic and the
application Accurate
application of
the topic to
the
information
technology Accurate and
comprehensive
application of
the topic to
the
information
technology Detail and
complete
account of
application
of the topic
to the
information
technology
Understanding of the
Information technology
application(s)
6 marks Inaccurate
description
of application
of
information
Technology Basic
description
of
application
of
information
Technology Accurate
description
of application
of
information
Technology Accurate
description of
application of
information
Technology
and some
connections
with relevant
topics Polished
description
of
application
of
information
Technology
and
references to
relevant
theories
Detail description of the
choice of the application
7 marks Little or no
evidence is
given for the
choice and
omit. Basic
evidence is
given for the
choice. Accurate
evidence is
given for the
choice. Accurate
evidence is
given for the
choice and
omit with
relevant
analysis Accurate
evidence is
given for the
choice and
omit with
relevant
analysis and
complete
detail

@ -0,0 +1,129 @@
Assessment 3 Instructions INT2024
Design a plane for
computer
implementation
7 marks Plane is not
designed in a
proper
manner Plane is
designed in a
proper
manner and
no flow -chart
is given Plane is
designed in a
proper
manner and
flow -chart is
also given Accurate and
comprehensive
plane is given
with a correct
flow -chart Appropriate
plane with
correct flow -
chart and
complete
detail is
given.
Writing an algorithm
7 marks Inaccurate
algorithm or
algorithm is
given in an
inappropriate
manner Correct
algorithm but
written in an
inappropriate
manner Correct
algorithm
which is
written in an
appropriate
manner Correct
algorithm
which is
written in an
inappropriate
manner with
little discussion Correct
algorithm
which is
written in an
inappropriate
manner with
complete
discussion
Conclusions
7 mark s Little or no
evidence of
accuracy of
the algorithm Basic
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of
the algorithm Accurate
evidence of
accuracy of the
algorithm Complete
analysis and
justification
of accuracy
of the
algorithm
Presentation
1 mark Poorly
organised
report with
unclear
structure Well
organised
report but
with some
errors Clearly
organised
report with
few errors Clearly
organised
report and
good use of
tables and
graphs Polished
report and
creative use
of tables and
graphs
Presentation
5 marks Poor quality
of slides or
poor
performance
of
presentation Well
prepared
presentation
but some
error Well
prepared
presentation
but one or
two error Well prepared
presentation
without an
error and
explain
everything
correctly Well
prepared
presentation
without an
error and
explain
everything
explicitly
with quality.

@ -0,0 +1,15 @@
Assessment 3 Instructions INT2024
Total Mark: / 25
25%
COMMENTS:

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

@ -2,12 +2,10 @@ import string
import hashlib
import random
import os
from PyPDF2 import PdfReader
from PyPDF2 import PdfReader, PdfWriter
from config import *
import re
FILE_NAME = 'manjil.pdf'
FILE_PATH = os.path.join(os.getcwd(), FILE_NAME)
def random_string_generator(string_length: int) -> str:
letters = string.ascii_letters
@ -17,15 +15,6 @@ def random_string_generator(string_length: int) -> str:
def hash_string(string_value: str) ->str:
return hashlib.sha256(string_value.encode('utf-8')).hexdigest()
def read_pdf_human_readable(file_path: str) -> list[str]:
pdf_page_text_contents: list = []
reader: PdfReader = PdfReader(file_path)
for i, page in enumerate(reader.pages):
text: str = page.extract_text()
if text:
pdf_page_text_contents.append(text.strip())
return pdf_page_text_contents
def is_valid_email(email):
pattern = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
if re.match(pattern, email):
@ -63,3 +52,28 @@ def password_check_sanity(passwd: str) -> bool:
class InsecurePasswordException(Exception):
pass
def split_pdf_into_pages_with_text(pdf_path: str, output_directory: str) -> int:
print("SPLIT PDF INTO PAGES WITH TEXT")
with open(pdf_path, 'rb') as pdf_file:
reader = PdfReader(pdf_file)
page_counter = 1
for page_num in range(len(reader.pages)):
page = reader.pages[page_num]
text = page.extract_text()
if text is None:
text = ''
output_txt_filename = os.path.join(output_directory, f"{page_counter}.txt")
with open(output_txt_filename, 'w', encoding='utf-8') as output_file:
output_file.write(text)
# Save as PDF file
writer = PdfWriter()
writer.add_page(page)
output_pdf_filename = os.path.join(output_directory, f"{page_counter}.pdf")
with open(output_pdf_filename, 'wb') as output_pdf_file:
writer.write(output_pdf_file)
if len(reader.pages) == page_counter:
return len(reader.pages)
page_counter += 1

@ -0,0 +1 @@
declare module 'js-cookie';

@ -1,4 +1,21 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '**',
port: '',
pathname: '**',
search: '',
},
],
},
webpack: (config) => {
config.resolve.alias.canvas = false;
return config;
},
};
export default nextConfig;

File diff suppressed because it is too large Load Diff

@ -3,21 +3,42 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"dev": "next dev -H 0.0.0.0",
"build": "next build",
"start": "next start",
"start": "next start -H 0.0.0.0",
"lint": "next lint"
},
"dependencies": {
"@radix-ui/react-alert-dialog": "^1.1.4",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.4",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-navigation-menu": "^1.2.3",
"@radix-ui/react-progress": "^1.1.1",
"@radix-ui/react-radio-group": "^1.2.2",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-select": "^2.1.4",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.2",
"@radix-ui/react-toast": "^1.2.4",
"@radix-ui/react-tooltip": "^1.1.6",
"@tanstack/react-table": "^8.20.6",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"edu-connect": "file:",
"js-cookie": "^3.0.5",
"lucide-react": "^0.471.0",
"next": "14.2.23",
"pdfjs-dist": "^4.8.69",
"react": "^18",
"react-dom": "^18",
"react-pageflip": "^2.0.3",
"react-pdf": "^9.2.1",
"swr": "^2.3.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7"
},

@ -0,0 +1,113 @@
import DataTable from "@/components/(dashboard)/common/DataTable/DataTable"
import { Button } from "@/components/(dashboard)/ui/button"
import { Badge } from "@/components/ui/badge"
import { routes } from "@/lib/routes"
import { ColumnDef } from "@tanstack/react-table"
import { ArrowUpDown } from "lucide-react"
const CategoryTable: React.FC<{
mutate: () => void
Data: Array<any>
isLoading: boolean
}> = ({
mutate,
Data,
isLoading
}) => {
const columns: ColumnDef<any>[] = [
{
accessorKey: "sn",
header: "SN",
cell: ({ row }) => (
<div className="capitalize">{row.index + 1}</div>
),
},
{
id: 'name',
accessorFn: (row: any) => row.original?.name,
header: ({ column }) => (
<Button
variant="ghost"
className="!px-0"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div className="capitalize">
<p>{row.original?.name}</p>
</div>
),
},
{
id: 'description',
accessorFn: (row: any) => row.original?.description,
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="!px-0"
>
Description
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div className="max-w-[300px] truncate">{row.original?.description ?? '-'}</div>
),
},
{
id: 'creationDate',
accessorFn: (row: any) => row.original?.creationDate,
header: ({ column }) => (
<Button
variant="ghost"
className="!px-0"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Created Date
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div className="whitespace-nowrap">
{row.original?.creationDate ? new Date(row.original.creationDate).toLocaleDateString() : '-'}
</div>
),
},
{
id: 'isActive',
accessorFn: (row: any) => row.original?.isActive,
header: ({ column }) => (
<Button
variant="ghost"
className="!px-0"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Status
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div>{row.original?.isActive ? <Badge variant={'success'}>Active</Badge> : <Badge variant={'destructive'}>Deactive</Badge>}</div>
),
},
]
return(
<>
<DataTable
data={Data}
columns={columns}
mutate={mutate}
searchKey="name"
isLoading={isLoading}
/>
</>
)
}
export default CategoryTable

@ -0,0 +1,49 @@
'use client'
import BreadCrumbNav from "@/components/(dashboard)/common/BreadCumbNav/BreadCrumbNav"
import ContentContainer from "@/components/(dashboard)/elements/ContentContainer"
import { PageHeading } from "@/components/(dashboard)/ui/title"
import CommonContainer from "@/components/elements/CommonContainer"
import AppContextProvider from "@/helpers/context/AppContextProvider"
import { defaultFetcher } from "@/helpers/fetch.helper"
import { routes } from "@/lib/routes"
import { APP_BASE_URL } from "@/utils/constants"
import AdminView from "@/views/AdminView"
import useSWR from "swr"
import CategoryTable from "./_partials/CategoryTable"
const CategoryIndexPage = () => {
const CategoryListURL = `${APP_BASE_URL}/api/courses/getCategories`
const { data, mutate, isLoading } = useSWR(CategoryListURL, defaultFetcher);
return(
<AppContextProvider>
<AdminView>
<CommonContainer>
<PageHeading>Categories</PageHeading>
<BreadCrumbNav breadCrumbItems={[
{
title: 'Dashboard',
href: routes.DASHBOARD_ROUTE
},
{
title: 'Categories',
href: routes.CATEGORY_INDEX_PAGE
},
]}/>
<ContentContainer>
<div>
<CategoryTable
mutate={mutate}
isLoading={isLoading}
Data={data || []}
/>
</div>
</ContentContainer>
</CommonContainer>
</AdminView>
</AppContextProvider>
)
}
export default CategoryIndexPage

@ -0,0 +1,172 @@
import DataTable from "@/components/(dashboard)/common/DataTable/DataTable"
import { Button } from "@/components/(dashboard)/ui/button"
import { Avatar, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { ColumnDef } from "@tanstack/react-table"
import { ArrowUpDown } from "lucide-react"
const CourseTable: React.FC<{
mutate: () => void
Data: Array<any>
isLoading: boolean
}> = ({
mutate,
Data,
isLoading
}) => {
const columns: ColumnDef<any>[] = [
{
accessorKey: "sn",
header: "SN",
cell: ({ row }) => (
<div className="capitalize">{row.index + 1}</div>
),
},
{
id: 'name',
accessorFn: (row: any) => row.original?.name,
header: ({ column }) => (
<Button
variant="ghost"
className="!px-0"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div className="capitalize flex gap-2">
<Avatar className="h-8 w-8">
<AvatarImage
src={row.original?.coverImage ?? 'no image path'}
alt={row.original?.name}
/>
</Avatar>
<p>{row.original?.name}</p>
</div>
),
},
{
id: 'description',
accessorFn: (row: any) => row.original?.description,
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="!px-0"
>
Description
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div className="max-w-[300px] truncate">{row.original?.description ?? '-'}</div>
),
},
{
id: 'author',
accessorFn: (row: any) => row.original?.author?.firstName,
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="!px-0"
>
Author
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div>{row.original?.author?.firstName} {row.original?.author?.lastName}</div>
),
},
{
id: 'category',
accessorFn: (row: any) => row.original?.category?.name,
header: ({ column }) => (
<Button
variant="ghost"
className="!px-0"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Category
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div className="capitalize">{row.original?.category?.name}</div>
),
},
{
id: 'creationDate',
accessorFn: (row: any) => row.original?.creationDate,
header: ({ column }) => (
<Button
variant="ghost"
className="!px-0"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Published Date
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div className="whitespace-nowrap">
{row.original?.creationDate ? new Date(row.original.creationDate).toLocaleDateString() : '-'}
</div>
),
},
{
id: 'isActive',
accessorFn: (row: any) => row.original?.isActive,
header: ({ column }) => (
<Button
variant="ghost"
className="!px-0"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Status
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div>{row.original?.isActive ? <Badge variant={'success'}>Active</Badge> : <Badge variant={'destructive'}>Deactive</Badge>}</div>
),
},
{
id: 'totalEnrolled',
accessorFn: (row: any) => row.original?.totalEnrolled,
header: ({ column }) => (
<Button
variant="ghost"
className="!px-0"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Students
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<div>
{row.original?.totalEnrolled ?? '-'}
</div>
),
},
]
return(
<>
<DataTable
data={Data}
columns={columns}
mutate={mutate}
searchKey="name"
isLoading={isLoading}
/>
</>
)
}
export default CourseTable

@ -0,0 +1,48 @@
'use client'
import BreadCrumbNav from "@/components/(dashboard)/common/BreadCumbNav/BreadCrumbNav"
import ContentContainer from "@/components/(dashboard)/elements/ContentContainer"
import { PageHeading } from "@/components/(dashboard)/ui/title"
import CommonContainer from "@/components/elements/CommonContainer"
import AppContextProvider from "@/helpers/context/AppContextProvider"
import { defaultFetcher } from "@/helpers/fetch.helper"
import { routes } from "@/lib/routes"
import { APP_BASE_URL } from "@/utils/constants"
import AdminView from "@/views/AdminView"
import useSWR from "swr"
import CourseTable from "./_partials/CourseTable"
const CourseIndexPage = () => {
const CourseListURL = `${APP_BASE_URL}/api/course/listAll`
const { data, mutate, isLoading } = useSWR(CourseListURL, defaultFetcher);
return (
<AppContextProvider>
<AdminView>
<CommonContainer>
<PageHeading>Courses</PageHeading>
<BreadCrumbNav breadCrumbItems={[
{
title: 'Dashboard',
href: routes.DASHBOARD_ROUTE
},
{
title: 'Courses',
href: routes.COURSE_INDEX_PAGE
},
]}/>
<ContentContainer>
<div>
<CourseTable
mutate={mutate}
isLoading={isLoading}
Data={data?.data || []}
/>
</div>
</ContentContainer>
</CommonContainer>
</AdminView>
</AppContextProvider>
)
}
export default CourseIndexPage

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save