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. 183
      backend/blueprints/admin/__init__.py
  5. 49
      backend/blueprints/badge/__init__.py
  6. 152
      backend/blueprints/chat/__init__.py
  7. 541
      backend/blueprints/course/__init__.py
  8. 141
      backend/blueprints/profile/__init__.py
  9. 71
      backend/blueprints/public/__init__.py
  10. 228
      backend/blueprints/quiz/__init__.py
  11. 1
      backend/blueprints/session/__init__.py
  12. 4
      backend/config.py
  13. 6
      backend/constants/__init__.py
  14. 16
      backend/db/model.py
  15. BIN
      backend/requirements.txt
  16. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf
  17. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/1.pdf
  18. 34
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/1.txt
  19. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/2.pdf
  20. 45
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/2.txt
  21. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/3.pdf
  22. 63
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/3.txt
  23. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/4.pdf
  24. 128
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/4.txt
  25. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/5.pdf
  26. 129
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/5.txt
  27. BIN
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/6.pdf
  28. 15
      backend/uploads/CrKZMGYNpfofiwwyZjzAPoAlKpJHIYYn.pdf_parts/6.txt
  29. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf
  30. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/1.pdf
  31. 34
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/1.txt
  32. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/2.pdf
  33. 45
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/2.txt
  34. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/3.pdf
  35. 63
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/3.txt
  36. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/4.pdf
  37. 128
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/4.txt
  38. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/5.pdf
  39. 129
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/5.txt
  40. BIN
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/6.pdf
  41. 15
      backend/uploads/CyPqrZgrAeCMNWawSfEvwJFTxgaLuJav.pdf_parts/6.txt
  42. BIN
      backend/uploads/JRyiqfKMGnWcunvfcosPJxgBGQTjasLh.jpg
  43. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf
  44. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/1.pdf
  45. 34
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/1.txt
  46. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/2.pdf
  47. 45
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/2.txt
  48. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/3.pdf
  49. 63
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/3.txt
  50. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/4.pdf
  51. 128
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/4.txt
  52. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/5.pdf
  53. 129
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/5.txt
  54. BIN
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/6.pdf
  55. 15
      backend/uploads/TbjYLfSVjZSrBtnLxFaAoZLlRyzrHJEV.pdf_parts/6.txt
  56. BIN
      backend/uploads/ToasQcxJytcIEzWUREfvPfkVEmFAYPRT.jpeg
  57. BIN
      backend/uploads/ZFRQjJYWCKWLQQSpPJRZRcAvGFtqzFBd.jpeg
  58. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf
  59. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/1.pdf
  60. 34
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/1.txt
  61. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/2.pdf
  62. 45
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/2.txt
  63. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/3.pdf
  64. 63
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/3.txt
  65. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/4.pdf
  66. 128
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/4.txt
  67. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/5.pdf
  68. 129
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/5.txt
  69. BIN
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/6.pdf
  70. 15
      backend/uploads/ayjGosqfWMPHKTIyeeaeGwvlRutoshUb.pdf_parts/6.txt
  71. BIN
      backend/uploads/bIhlRmHCOUumtwXCFBbVipXOEvgFvBNN.jpeg
  72. BIN
      backend/uploads/cTWekssdRoWYgpXYaHJsyBPtKwdbeBOL.jpeg
  73. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf
  74. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/1.pdf
  75. 34
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/1.txt
  76. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/2.pdf
  77. 45
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/2.txt
  78. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/3.pdf
  79. 63
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/3.txt
  80. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/4.pdf
  81. 128
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/4.txt
  82. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/5.pdf
  83. 129
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/5.txt
  84. BIN
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/6.pdf
  85. 15
      backend/uploads/gVnryGmAkJesQziGcGjqjScHCXWVqMxL.pdf_parts/6.txt
  86. BIN
      backend/uploads/izvjgAZCUwVlTZpoMdoFUnMoMoPNDkPD.pdf
  87. BIN
      backend/uploads/learn-with-us.jpg
  88. BIN
      backend/uploads/uBYfyzaCrsAeHTYdKnPfmVVcahWaTVte.jpeg
  89. BIN
      backend/uploads/user_3e7bb78f-a0e8-42f2-bd9f-6792369e3e64_meme.jpg
  90. BIN
      backend/uploads/user_f7ab56f1-d692-409e-98ab-2a563f37e389_1638856701024.jpg
  91. 38
      backend/utils/utils.py
  92. 1
      frontend/edu-connect/js-cookie.d.ts
  93. 19
      frontend/edu-connect/next.config.mjs
  94. 2145
      frontend/edu-connect/package-lock.json
  95. 26
      frontend/edu-connect/package.json
  96. 113
      frontend/edu-connect/src/app/(admin)/admin/category/_partials/CategoryTable.tsx
  97. 49
      frontend/edu-connect/src/app/(admin)/admin/category/page.tsx
  98. 172
      frontend/edu-connect/src/app/(admin)/admin/course/_partials/CourseTable.tsx
  99. 48
      frontend/edu-connect/src/app/(admin)/admin/course/page.tsx
  100. 20
      frontend/edu-connect/src/app/(admin)/admin/dashboard/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/ .venv/
__pypackages__/ __pypackages__/
**.env
# Node.js # Node.js
node_modules/ 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 datetime import datetime
from constants import UserRole from constants import UserRole
from utils.utils import random_string_generator, hash_string from utils.utils import random_string_generator, hash_string
from flask_cors import CORS
from blueprints.profile import profile as profileBlueprint from blueprints.profile import profile as profileBlueprint
from blueprints.session import session as sessionBlueprint 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__) 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 # 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 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(profileBlueprint, url_prefix='/api/profile')
app.register_blueprint(sessionBlueprint,url_prefix='/api/session') 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>') @app.route('/media/<string:filename>')
def send_file(filename): def send_file(filename):
return send_from_directory(USER_UPLOADS_DIR, 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']) @app.route('/', methods=['GET', 'POST'])
def homepage(): def homepage():
return {'message': 'Welcome back !'}, 200 return {'message': 'Welcome back !'}, 200
@ -53,7 +83,7 @@ def seed_data():
joinedDate=datetime.utcnow(), lastOnline=datetime.utcnow(), joinedDate=datetime.utcnow(), lastOnline=datetime.utcnow(),
bio=f"This is user{i}'s bio.", role=int(UserRole.USER), bio=f"This is user{i}'s bio.", role=int(UserRole.USER),
isActivated=True, sessions=[], user_badges=[], isActivated=True, sessions=[], user_badges=[],
enrollments=[], quizzes=[], quiz_attempts=[], chats=[], notifications=[]) enrollments=[], quizzes=[], quiz_attempts=[], chats=[], notifications=[],publications=[])
for i in range(1, 6) ] for i in range(1, 6) ]
db.session.add_all(users) db.session.add_all(users)
db.session.commit() db.session.commit()

@ -0,0 +1,183 @@
from utils .auth import auth_required, requires_role
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
from constants import UserRole
admin = Blueprint('admin', __name__)
@admin.route('/stats/users', methods=['GET'])
@auth_required()
@requires_role([UserRole.ADMIN])
def get_user_stats():
"""
Get total users and authors count.
Only accessible by admin users.
"""
try:
# Get total users
total_users = db.session.execute(
select(func.count()).select_from(User)
).scalar()
# Get authors (users who have created courses)
distinct_authors_count = db.session.execute(
select(func.count(func.distinct(Course.authorID)))
).scalar()
return jsonify({
'stats': {
'totalUsers': total_users,
'totalAuthors': distinct_authors_count
}
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@admin.route('/stats/enrollments', methods=['GET'])
@auth_required()
@requires_role([UserRole.ADMIN])
def get_enrollment_stats():
"""
Get course enrollment and discussion statistics.
Only accessible by admin users.
"""
try:
# Get enrollment and user counts
enrollment_stats = db.session.execute(
select(
func.count(Enrollment.id).label('total_enrollments'),
func.count(func.distinct(Enrollment.userID)).label('enrolled_users')
)
.select_from(Enrollment)
).first()
# Get course-wise enrollment counts
course_stats = db.session.execute(
select(
Course.name,
func.count(Enrollment.id).label('enrollment_count')
)
.join(Course, Course.id == Enrollment.courseID)
.group_by(Course.id)
.order_by(desc('enrollment_count'))
).all()
return jsonify({
'stats': {
'totalEnrollments': enrollment_stats.total_enrollments,
'totalEnrolledUsers': enrollment_stats.enrolled_users,
'courseEnrollments': [{
'courseName': stat.name,
'enrollmentCount': stat.enrollment_count
} for stat in course_stats]
}
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@admin.route('/stats/discussions', methods=['GET'])
@auth_required()
@requires_role([UserRole.ADMIN])
def get_discussion_stats():
"""
Get chat room activity statistics.
Only accessible by admin users.
"""
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(
Course.name,
func.count(Chat.id).label('message_count'),
func.count(func.distinct(Chat.userID)).label('active_users')
)
.join(Course, Course.id == Chat.courseID)
.where(Chat.chatDate >= twenty_four_hours_ago)
.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:
most_active = active_rooms[0]
most_active_room = {
'name': most_active.name,
'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)))
.where(Chat.chatDate >= twenty_four_hours_ago)
).scalar()
return jsonify({
'stats': {
'totalActiveRooms': total_active_rooms,
'totalActiveUsers': total_active_users,
'mostActiveRoom': most_active_room,
'activeRooms': [{
'roomName': room.name,
'messageCount': room.message_count,
'activeUsers': room.active_users
} 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()
@requires_role([UserRole.ADMIN])
def get_user_details():
"""
Get detailed information for all users.
Only accessible by admin users.
"""
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),
'username': user.username,
'email': user.email,
'firstName': user.firstName,
'lastName': user.lastName,
'profilePicture': url_for('send_file', filename=current_user.pfpFilename, _external=True),
'bio': user.bio,
'role': user.role,
'isActivated': user.isActivated,
'joinedDate': user.joinedDate.isoformat(),
'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,3 +1,48 @@
from flask import Blueprint 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
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.route('/myBadges')
@auth_required()
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,66 +1,114 @@
from flask import Blueprint, request, jsonify import uuid
from flask import Blueprint, request, jsonify,g
from uuid import UUID from uuid import UUID
from db.model import db, User, Course, Enrollment 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 = Blueprint('chat', __name__)
@chat.route('/course/<uuid:course_id>/users', methods=['POST']) @chat.route("/send", methods=["POST"])
def get_users_assigned_to_course(course_id: UUID): @auth_required()
""" def create_chat():
Fetch all users assigned to a specific course. current_user: User = g.current_user # Fetch the logged-in user
:param course_id: ID of the course to fetch users for (UUID). data = request.form
:return: JSON response with users assigned to the course. 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: try:
# Query the course to ensure it exists # Check if the user is enrolled in the course
course = Course.query.get(course_id) enrollment_record: Enrollment = db.session.execute(
if not course: select(Enrollment).where(and_(
return jsonify({"error": "Course not found."}), 404 Enrollment.courseID == data.get("course_id"),
Enrollment.userID == current_user.id)
# Get the list of users assigned to the course )
users = User.query.filter(User.enrollments.any(course_id=course_id)).all() ).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()
# Prepare the response data return jsonify({"message": "Chat sent successfully.", "chat_id": str(new_chat.id)}), 201
user_data = [
{
"id": user.id,
"email": user.email,
"username": user.username,
"firstName": user.firstName,
"lastName": user.lastName,
}
for user in users
]
return jsonify({"course": course.name, "users": user_data}), 200
except Exception as e: except Exception as e:
return jsonify({"error": f"An error occurred: {str(e)}"}), 500 return jsonify({"error": f"An error occurred: {str(e)}"}), 500
@chat.route("/get", methods=["GET", "POST"])
@chat.route('/course/<uuid:course_id>/chat', methods=['POST']) @auth_required()
def join_chat(course_id: UUID): def get_messages():
"""
Allow users to join the chat only if they are enrolled in the course.
:param course_id: ID of the course (UUID).
:return: JSON response indicating whether the user can join or not.
"""
try: try:
# Get the user from the request (assume user_id is passed in the request body) course_id: uuid.UUID = uuid.UUID(request.form.get('course_id'))
user_id = request.json.get('user_id') current_user: User = g.current_user
if not user_id: limit = int(request.form.get('limit', 10))
return jsonify({"error": "User ID is required."}), 400 before_id = request.form.get('before')
after_id = request.form.get('after')
# Query the user and ensure they exist # Verify user's enrollment
user = User.query.get(user_id) enrollment = db.session.execute(
if not user: select(Enrollment).where(
return jsonify({"error": "User not found."}), 404 and_(Enrollment.courseID == course_id, Enrollment.userID == current_user.id)
)
# Check if the user is enrolled in the course ).scalar()
enrollment = Enrollment.query.filter_by(userID=user_id, courseID=course_id).first()
if not enrollment: if not enrollment:
return jsonify({"error": "User is not enrolled in this course."}), 403 return jsonify({"error": "You are not enrolled in this course."}), 403
query = select(Chat).where(Chat.courseID == course_id)
if before_id:
try:
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.order_by(Chat.chatDate.desc()).where(Chat.chatDate < reference_message.chatDate)
except ValueError:
return jsonify({'message': 'Invalid message ID format'}), 400
elif after_id:
try:
reference_message = db.session.execute(
select(Chat).where(Chat.id == uuid.UUID(after_id))
).scalar()
# If enrolled, allow the user to join the chat if not reference_message:
return jsonify({"message": f"User {user.username} is enrolled in the course and can join the chat."}), 200 return jsonify({'message': 'Reference message not found'}), 404
query = query.order_by(Chat.chatDate.asc()).where(Chat.chatDate > reference_message.chatDate)
except ValueError:
return jsonify({'message': 'Invalid message ID format'}), 400
else:
query = query.order_by(Chat.chatDate.desc())
query = query.limit(limit)
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(),
'isSelf': int(msg.userID == g.current_user.id)
} for msg in messages]
return jsonify({
'messages': chat_messages,
'count': len(chat_messages),
}), 200
except Exception as e: except Exception as e:
return jsonify({"error": f"An error occurred: {str(e)}"}), 500 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 from werkzeug.datastructures import MultiDict
import os import os
import uuid import uuid
import math
from config import DEFAULT_COURSE_COVER from config import DEFAULT_COURSE_COVER
from db.model import db, Course, Category, User, Chat from db.model import db, Course, Category, User, Chat, Enrollment
from utils.utils import random_string_generator from utils.utils import random_string_generator, split_pdf_into_pages_with_text
from utils.auth import auth_required, requires_role from utils.auth import auth_required, requires_role
from constants import * from constants import *
from config import * from config import *
from constants import PublishedStatus from constants import PublishedStatus
from typing import Union
from db.model import UserRole
course = Blueprint('course', __name__) 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() @auth_required()
def create_course(): def create_course():
form_data: dict = request.form form_data: dict = request.form
course_uploaded_cover_image: MultiDict|None = request.files.get('cover_image', None) course_uploaded_cover_image: MultiDict|None = request.files.get('cover_image', None)
course_uploaded_pdf: MultiDict|None = request.files.get('course_pdf', None) course_uploaded_pdf: MultiDict|None = request.files.get('course_pdf', None)
cover_file_name: str = DEFAULT_COURSE_COVER cover_file_name: str = DEFAULT_COURSE_COVER
pdf_file_name: str = '' pdf_file_name: str = ''
pdf_total_pages: int = 1
if course_uploaded_cover_image is not None: 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)) course_uploaded_cover_image.save(os.path.join(USER_UPLOADS_DIR, cover_file_name))
if course_uploaded_pdf is not None: 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)) 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: try:
course_name: str = form_data['course_name'] course_name: str = form_data['course_name']
except KeyError: except KeyError:
return jsonify({'message': 'Course name cannot be empty'}), 401 return jsonify({'message': 'Course name cannot be empty'}), 401
course_description: str = form_data.get('course_description', '') course_description: str = form_data.get('course_description', '')
category_id: uuid.UUID = uuid.UUID(form_data['category_uuid']) 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( new_course: Course = Course(
name=course_name, name=course_name,
categoryID=category_id, categoryID=category_id,
authorID=g.current_user.id, authorID=g.current_user.id,
category=category,
author=g.current_user,
description=course_description, description=course_description,
isActive=True, isActive=True,
pageForCommunity=page_for_community,
publishedStatus=int(published_status), publishedStatus=int(published_status),
coverImage=cover_file_name, coverImage=cover_file_name,
serverFilename =pdf_file_name, serverFilename=pdf_file_name,
totalPages=pdf_total_pages,
enrollments=[], enrollments=[],
quizzes=[], quizzes=[],
chats=[] chats=[]
) )
# chat: Chat = Chat(courseID=new_course.id) TODO: Add a welcome chat for this course # 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() 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,5 +1,6 @@
from email.policy import default from email.policy import default
from flask import Blueprint, request, jsonify, current_app, g,url_for from flask import Blueprint, request, jsonify, current_app, g,url_for
from sqlalchemy import select
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from datetime import datetime from datetime import datetime
from utils.auth import auth_required, requires_role from utils.auth import auth_required, requires_role
@ -15,7 +16,6 @@ from sqlalchemy.exc import IntegrityError
# from flask import url_for # from flask import url_for
profile = Blueprint('profile', __name__) profile = Blueprint('profile', __name__)
# Function to check allowed file extensions
def allowed_file(filename): def allowed_file(filename):
"""Check if the uploaded file has an allowed extension.""" """Check if the uploaded file has an allowed extension."""
return '.' in filename and filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS'] return '.' in filename and filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS']
@ -103,39 +103,71 @@ def register():
return jsonify({"error": "Registration failed, please try again later."}), 500 return jsonify({"error": "Registration failed, please try again later."}), 500
#make a get request to get json on hello word #make a get request to get json on hello word
@profile.route('/me', methods=['GET']) @profile.route('/me', methods=['GET', 'PUT'])
@auth_required() @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 current_user: User = g.current_user
try: if g.current_user.role == int(UserRole.ADMIN) and request.form.get('user_id'):
target_user: User = db.session.execute(
profile_url = url_for('send_file', filename=current_user.pfpFilename, _external=True) select(User).where(User.id == uuid.uuid(request.form.get('user_id')))
except: ).scalar()
profile_url = "" if not target_user:
try: return jsonify({'message': 'User not found'}), 404
# Construct the user profile data else:
profile_data = { current_user = target_user
"id": str(current_user.id), if request.method == 'GET':
"email": current_user.email, profile_picture = url_for('send_file', filename=current_user.pfpFilename, _external=True)
"first_name": current_user.firstName, try:
"last_name": current_user.lastName, # Construct the user profile data
"username": current_user.username, profile_data = {
"dob": current_user.dob.isoformat(), "id": str(current_user.id),
"joined_date": current_user.joinedDate.isoformat(), "email": current_user.email,
"last_online": current_user.lastOnline.isoformat(), "firstName": current_user.firstName,
"bio": current_user.bio, "lastName": current_user.lastName,
"role": current_user.role, "username": current_user.username,
"pfp_filename": current_user.pfpFilename, "dob": current_user.dob.isoformat() if current_user.dob else None,
"profile_url":profile_url "joined_date": current_user.joinedDate.isoformat(),
} "last_online": current_user.lastOnline.isoformat(),
"bio": current_user.bio,
return jsonify({"profile": profile_data}), 200 "role": current_user.role,
except Exception as e: "pfp_filename": current_user.pfpFilename,
return jsonify({"error": f"Failed to fetch profile. Error: {str(e)}"}), 500 "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']) @profile.route('/update-profile-picture', methods=['PATCH'])
@ -157,22 +189,18 @@ def update_profile_picture():
return jsonify({"error": "No selected file"}), 400 return jsonify({"error": "No selected file"}), 400
if not allowed_file(file.filename): if not allowed_file(file.filename):
return jsonify({"error": "Invalid file type"}), 400 return jsonify({"error": "Invalid file type"}), 400
# Secure the filename and save the new file # Secure the filename and save the new file
filename = secure_filename(f"user_{user.id}_{file.filename}") filename = secure_filename(f"user_{user.id}_{file.filename}")
new_filepath = os.path.join(USER_UPLOADS_DIR, filename) new_filepath = os.path.join(USER_UPLOADS_DIR, filename)
file.save(new_filepath) file.save(new_filepath)
# Delete the old profile picture (if it's not the default) # Delete the old profile picture (if it's not the default)
if user.pfpFilename != DEFAULT_PROFILE_FILE: if user.pfpFilename != DEFAULT_PROFILE_FILE:
old_filepath = os.path.join(USER_UPLOADS_DIR, user.pfpFilename) old_filepath = os.path.join(USER_UPLOADS_DIR, user.pfpFilename)
if os.path.exists(old_filepath): if os.path.exists(old_filepath):
os.remove(old_filepath) os.remove(old_filepath)
# Update the user's profile picture # Update the user's profile picture
user.pfpFilename = filename user.pfpFilename = filename
db.session.commit() db.session.commit()
# Generate the new profile URL # Generate the new profile URL
profile_url = url_for('send_file',filename=user.pfpFilename,_external=True) profile_url = url_for('send_file',filename=user.pfpFilename,_external=True)
@ -184,4 +212,47 @@ def allowed_file(filename):
Validate file extensions. Validate file extensions.
""" """
allowed_extensions = {'png', 'jpg', 'jpeg', 'gif'} 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

@ -0,0 +1,71 @@
from flask import Blueprint, jsonify
from db.model import User, Course, db
from sqlalchemy import select, func
public_summary = Blueprint('public', __name__)
@public_summary.route('/stats/total-users', methods=['GET'])
def get_total_users():
"""
Fetch total user count.
"""
try:
total_users = db.session.execute(
select(func.count()).select_from(User)
).scalar()
return jsonify({'totalUsers': total_users}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@public_summary.route('/stats/total-authors', methods=['GET'])
def get_total_authors():
"""
Fetch total authors (users who have created courses).
"""
try:
total_authors = db.session.execute(
select(func.count(func.distinct(Course.authorID)))
.select_from(Course)
).scalar()
return jsonify({'totalAuthors': total_authors}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@public_summary.route('/stats/total-courses', methods=['GET'])
def get_total_courses():
"""
Fetch total course count.
"""
try:
total_courses = db.session.execute(
select(func.count()).select_from(Course)
).scalar()
return jsonify({'totalCourses': total_courses}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@public_summary.route('/stats/subscribed-users', methods=['GET'])
def get_subscribed_users():
"""
Fetch count of users subscribed to the newsletter and are activated.
"""
try:
subscribed_users = db.session.execute(
select(func.count(User.email))
.select_from(User)
.where(User.isActivated == True)
).scalar()
return jsonify({'subscribedNewsletter': subscribed_users}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500

@ -0,0 +1,228 @@
import json
import os
import uuid
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('/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': '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(new_attempt)
if quiz_row.creatorUser.id == g.current_user.id:
quiz_row.creatorHasAttempted = True
db.session.commit()
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 target_session.isValid = False
db.session.commit() db.session.commit()
return jsonify({'message': 'Session invalidated'}), 200 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__)) PROJECT_ROOT: os.path = os.path.dirname(os.path.abspath(__file__))
USER_UPLOADS_DIR: str = os.path.abspath(os.path.join(PROJECT_ROOT, "uploads")) 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}" DB_URI: str = f"{DB_ENGINE}://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
ACTIVATE_ACCOUNTS_ON_SIGNUP: bool = True ACTIVATE_ACCOUNTS_ON_SIGNUP: bool = True

@ -15,10 +15,14 @@ class PublishedStatus(Enum):
REVOKED = 3 REVOKED = 3
BANNED = 4 BANNED = 4
DRAFT = 5 DRAFT = 5
def __int__(self):
return self.value
class NotificationTypes(Enum): class NotificationTypes(Enum):
MENTION = 0 MENTION = 0
COURSE_PUBLISH_STATUS_UPDATE = 1 COURSE_PUBLISH_STATUS_UPDATE = 1
NEW_BADGE = 2 NEW_BADGE = 2
TEXT_WITH_URL = 3 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 flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, MappedAsDataclass 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 from datetime import datetime
import uuid import uuid
from typing import List from typing import List
@ -73,8 +73,11 @@ class Course(db.Model):
authorID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id")) authorID: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id"))
author: Mapped["User"] = relationship(back_populates="publications") author: Mapped["User"] = relationship(back_populates="publications")
description: Mapped[str] = mapped_column(String(1024), nullable=False, default='') 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) 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()) creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now())
coverImage: Mapped[str] = mapped_column(String(256), nullable=False, default=DEFAULT_COURSE_COVER) coverImage: Mapped[str] = mapped_column(String(256), nullable=False, default=DEFAULT_COURSE_COVER)
serverFilename: Mapped[str] = mapped_column(String(256), nullable=False, default='') 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") quiz_attempts: Mapped[List["QuizAttempt"]] = relationship(back_populates="quiz", cascade="all, delete-orphan")
courseID: Mapped[uuid.UUID] = mapped_column(ForeignKey("course.id")) courseID: Mapped[uuid.UUID] = mapped_column(ForeignKey("course.id"))
course: Mapped["Course"] = relationship(back_populates="quizzes") 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()) creationDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now())
isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) isActive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
creatorHasAttempted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
class QuizAttempt(db.Model): class QuizAttempt(db.Model):
__tablename__ = 'quiz_attempts' __tablename__ = 'quiz_attempts'
@ -114,8 +120,8 @@ class QuizAttempt(db.Model):
user: Mapped["User"] = relationship(back_populates="quiz_attempts") user: Mapped["User"] = relationship(back_populates="quiz_attempts")
quizID: Mapped[uuid.UUID] = mapped_column(ForeignKey("quiz.id")) quizID: Mapped[uuid.UUID] = mapped_column(ForeignKey("quiz.id"))
quiz: Mapped["Quiz"] = relationship(back_populates="quiz_attempts") quiz: Mapped["Quiz"] = relationship(back_populates="quiz_attempts")
answerKey: Mapped[str] = mapped_column(String, nullable=False) userAnswer: Mapped[str] = mapped_column(String, nullable=False)
score: Mapped[int] = mapped_column(default=0, nullable=False) isCorrect: Mapped[int] = mapped_column(default=0, nullable=False)
attemptDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now()) attemptDate: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=func.now())
class Chat(db.Model): 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 hashlib
import random import random
import os import os
from PyPDF2 import PdfReader from PyPDF2 import PdfReader, PdfWriter
from config import * from config import *
import re import re
FILE_NAME = 'manjil.pdf'
FILE_PATH = os.path.join(os.getcwd(), FILE_NAME)
def random_string_generator(string_length: int) -> str: def random_string_generator(string_length: int) -> str:
letters = string.ascii_letters letters = string.ascii_letters
@ -17,15 +15,6 @@ def random_string_generator(string_length: int) -> str:
def hash_string(string_value: str) ->str: def hash_string(string_value: str) ->str:
return hashlib.sha256(string_value.encode('utf-8')).hexdigest() 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): def is_valid_email(email):
pattern = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$' pattern = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
if re.match(pattern, email): if re.match(pattern, email):
@ -63,3 +52,28 @@ def password_check_sanity(passwd: str) -> bool:
class InsecurePasswordException(Exception): class InsecurePasswordException(Exception):
pass 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} */ /** @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; export default nextConfig;

File diff suppressed because it is too large Load Diff

@ -3,20 +3,42 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev -H 0.0.0.0",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start -H 0.0.0.0",
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "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-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-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", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"js-cookie": "^3.0.5",
"lucide-react": "^0.471.0", "lucide-react": "^0.471.0",
"next": "14.2.23", "next": "14.2.23",
"pdfjs-dist": "^4.8.69",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"react-pageflip": "^2.0.3",
"react-pdf": "^9.2.1",
"swr": "^2.3.0",
"tailwind-merge": "^2.6.0", "tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7" "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

@ -0,0 +1,20 @@
'use client'
import CommonContainer from "@/components/(dashboard)/elements/CommonContainer"
import AppContextProvider from "@/helpers/context/AppContextProvider"
import AdminView from "@/views/AdminView"
const DashBoardIndexPage = () => {
return(
<>
<AppContextProvider>
<AdminView>
<CommonContainer>
<h2>hello</h2>
</CommonContainer>
</AdminView>
</AppContextProvider>
</>
)
}
export default DashBoardIndexPage

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

Loading…
Cancel
Save