Compare commits

..

7 Commits

Author SHA1 Message Date
fine4me aff6d939ca user reoutes 6 months ago
fine4me 0ba527a3fa added pdf seperation 6 months ago
fine4me d8b426fa33 made chat before and after 6 months ago
fine4me 971dbf2088 made public summary removed checkguard 6 months ago
fine4me 1ace9341b9 admin priviledge 6 months ago
fine4me 576214ce5d made changes in chat 6 months ago
PANDACUSHION d4855b9615 changes in quiz 6 months ago
  1. 1
      .gitignore
  2. 54
      README.md
  3. 34
      backend/app.py
  4. 11
      backend/blueprints/admin/__init__.py
  5. 196
      backend/blueprints/badge/__init__.py
  6. 170
      backend/blueprints/chat/__init__.py
  7. 541
      backend/blueprints/course/__init__.py
  8. 107
      backend/blueprints/notification/__init__.py
  9. 138
      backend/blueprints/profile/__init__.py
  10. 264
      backend/blueprints/quiz/__init__.py
  11. 1
      backend/blueprints/session/__init__.py
  12. 4
      backend/config.py
  13. 4
      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. 2138
      frontend/edu-connect/package-lock.json
  95. 27
      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,7 +26,6 @@ env/
.venv/
__pypackages__/
**.env
# Node.js
node_modules/

@ -1,53 +1 @@
# 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
# HackX Entry By FreeBug

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

@ -1,5 +1,5 @@
from utils .auth import auth_required, requires_role
from flask import Blueprint, jsonify, g, url_for
from flask import Blueprint, jsonify, g,url_for
from db.model import User, Course, Enrollment, Chat, db
from sqlalchemy import select, func, desc, and_
from datetime import datetime, timedelta
@ -22,14 +22,15 @@ def get_user_stats():
).scalar()
# Get authors (users who have created courses)
distinct_authors_count = db.session.execute(
authors = db.session.execute(
select(func.count(func.distinct(Course.authorID)))
.select_from(Course)
).scalar()
return jsonify({
'stats': {
'totalUsers': total_users,
'totalAuthors': distinct_authors_count
'totalAuthors': authors
}
}), 200
@ -139,6 +140,8 @@ def get_discussion_stats():
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])
@ -179,5 +182,3 @@ def get_user_details():
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500

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

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

@ -1,569 +1,56 @@
from flask import Blueprint, request, jsonify, g, url_for
from sqlalchemy import select, and_, func, distinct, or_
from sqlalchemy.exc import IntegrityError
from flask import Blueprint, request, jsonify, g
from werkzeug.datastructures import MultiDict
import os
import uuid
import math
from config import DEFAULT_COURSE_COVER
from db.model import db, Course, Category, User, Chat, Enrollment
from utils.utils import random_string_generator, split_pdf_into_pages_with_text
from db.model import db, Course, Category, User, Chat
from utils.utils import random_string_generator
from utils.auth import auth_required, requires_role
from constants import *
from config import *
from constants import PublishedStatus
from typing import Union
from db.model import UserRole
course = Blueprint('course', __name__)
@course.route('/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'])
@course.route('/create', methods=['POST'])
@auth_required()
def create_course():
form_data: dict = request.form
course_uploaded_cover_image: MultiDict|None = request.files.get('cover_image', None)
course_uploaded_pdf: MultiDict|None = request.files.get('course_pdf', None)
cover_file_name: str = DEFAULT_COURSE_COVER
pdf_file_name: str = ''
pdf_total_pages: int = 1
if course_uploaded_cover_image is not None:
print("COURSE COVER ATTACHED")
cover_file_name: str = random_string_generator(32)+"."+course_uploaded_cover_image.filename.split('.')[-1]
cover_file_name: str = random_string_generator(32)+course_uploaded_cover_image.filename.split('.')[-1]
course_uploaded_cover_image.save(os.path.join(USER_UPLOADS_DIR, cover_file_name))
if course_uploaded_pdf is not None:
print('PDF ATTACHED')
pdf_file_name: str = random_string_generator(32) +"."+ course_uploaded_pdf.filename.split('.')[-1]
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))
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
published_status: PublishedStatus = PublishedStatus.DRAFT
try:
course_name: str = form_data['course_name']
except KeyError:
return jsonify({'message': 'Course name cannot be empty'}), 401
course_description: str = form_data.get('course_description', '')
category_id: uuid.UUID = uuid.UUID(form_data['category_uuid'])
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()
pages_required_for_community: int = int(form_data['community_unlock_at_pages']) # TODO: Add this field to model
new_course: Course = Course(
name=course_name,
categoryID=category_id,
authorID=g.current_user.id,
category=category,
author=g.current_user,
description=course_description,
isActive=True,
pageForCommunity=page_for_community,
publishedStatus=int(published_status),
coverImage=cover_file_name,
serverFilename=pdf_file_name,
totalPages=pdf_total_pages,
serverFilename =pdf_file_name,
enrollments=[],
quizzes=[],
chats=[]
)
# chat: Chat = Chat(courseID=new_course.id) TODO: Add a welcome chat for this course
db.session.add(new_course)
db.session.add_all(new_course)
db.session.commit()
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
return jsonify({'message': 'Course was created successfully.'}), 200

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

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

@ -1,228 +1,52 @@
import json
import os
from flask import Blueprint
from flask import Blueprint, request, jsonify, current_app, g
from db.model import User, Category, Quiz, Course, Enrollment, QuizAttempt
from db.model import db
import 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_
from sqlalchemy import 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)
@quiz.route('/create', methods=['POST'])
def create_quiz():
data: dict = request.form
creator_user: User = g.current_user
course_id: uuid.UUID = uuid.UUID(data.get('courseID'))
enrollment_data: Enrollment = db.session.execute(select(Enrollment).where(and_(Enrollment.courseID == course_id, Enrollment.userID == creator_user.id))).scalar()
if not enrollment_data:
return jsonify({'message': 'You are not enrolled in this class'}), 401
get_quiz_data :str = '{"questions":[{"question":"What is the capital of France?","options":["Paris","London","Berlin","Madrid"],"answer":"Paris"},{"question":"What is 2 + 2?","options":["3","4","5","6"],"answer":"4"}]}'
new_quiz = Quiz(
creatorUserID = creator_user.id,
courseID = course_id,
quizJson = get_quiz_data,
quiz_attempts = []
)
db.session.add_all(new_quiz)
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)
return jsonify({'message': 'Course was created successfully.'}), 200
@quiz.route('/attempt', methods=['POST'])
def attempt_quiz():
data: dict = request.form
attempt_user: User = g.current_user
quiz_id: uuid.UUID = uuid.UUID(data.get('quizID'))
course_data: Enrollment = db.session.execute(select(Enrollment).where(and_(Enrollment.courseID == course_data, Enrollment.userID == attempt_user.id))).scalar()
if not course_data:
return jsonify({'message': 'You are not enrolled in this class'}), 401
answerKey: str = '{"questions":[{"question":"What is the capital of France?","options":["Paris","London","Berlin","Madrid"],"answer":"Paris"},{"question":"What is 2 + 2?","options":["3","4","5","6"],"answer":"4"}]}'
#function to calculate Score
score_value = 0
new_attempt = QuizAttempt(
userID = attempt_user,
quizID = quiz_id,
answerKey = answerKey,
score = score_value
)
db.session.add(new_attempt)
if quiz_row.creatorUser.id == g.current_user.id:
quiz_row.creatorHasAttempted = True
db.session.add_all(new_attempt)
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
return jsonify({'message': 'Quiz was appended sucessfully'}), 200

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

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

@ -15,8 +15,6 @@ class PublishedStatus(Enum):
REVOKED = 3
BANNED = 4
DRAFT = 5
def __int__(self):
return self.value
class NotificationTypes(Enum):
MENTION = 0
@ -24,5 +22,3 @@ class NotificationTypes(Enum):
NEW_BADGE = 2
TEXT_WITH_URL = 3
PLAINTEXT_NOTICE = 4
def __int__(self):
return self.value

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

Binary file not shown.

@ -1,34 +0,0 @@
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.

@ -1,45 +0,0 @@
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

@ -1,63 +0,0 @@
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

@ -1,128 +0,0 @@
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

@ -1,129 +0,0 @@
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.

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

@ -1,34 +0,0 @@
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.

@ -1,45 +0,0 @@
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

@ -1,63 +0,0 @@
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

@ -1,128 +0,0 @@
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

@ -1,129 +0,0 @@
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.

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

@ -1,34 +0,0 @@
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.

@ -1,45 +0,0 @@
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

@ -1,63 +0,0 @@
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

@ -1,128 +0,0 @@
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

@ -1,129 +0,0 @@
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.

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

@ -1,34 +0,0 @@
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.

@ -1,45 +0,0 @@
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

@ -1,63 +0,0 @@
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

@ -1,128 +0,0 @@
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

@ -1,129 +0,0 @@
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.

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

@ -1,34 +0,0 @@
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.

@ -1,45 +0,0 @@
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

@ -1,63 +0,0 @@
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

@ -1,128 +0,0 @@
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

@ -1,129 +0,0 @@
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.

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

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

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

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

File diff suppressed because it is too large Load Diff

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

@ -1,113 +0,0 @@
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

@ -1,49 +0,0 @@
'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

@ -1,172 +0,0 @@
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

@ -1,48 +0,0 @@
'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

@ -1,20 +0,0 @@
'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