diff --git a/submissions/T039_BlueScreeners/T039.pptx b/submissions/T039_BlueScreeners/T039.pptx new file mode 100644 index 00000000..f0fc8344 Binary files /dev/null and b/submissions/T039_BlueScreeners/T039.pptx differ diff --git a/submissions/T039_BlueScreeners/ai_services.py b/submissions/T039_BlueScreeners/ai_services.py new file mode 100644 index 00000000..cfb17bbd --- /dev/null +++ b/submissions/T039_BlueScreeners/ai_services.py @@ -0,0 +1,61 @@ +# ai_services.py +import joblib + +# --- Placeholder functions for your ML models --- +# You will replace these with the actual code from your model files. + +def load_prediction_model(path='ml_models/trained_model.pkl'): + """Loads the trained risk prediction model.""" + try: + model = joblib.load(path) + print("Prediction model loaded successfully.") + return model + except FileNotFoundError: + print("Prediction model file not found. Please check the path.") + return None + +def get_risk_prediction(model, student_data): + """ + Takes the model and student data to return a risk prediction. + - student_data should be a list or array in the format your model expects, + e.g., [attendance_percentage, avg_assignment_score, avg_quiz_score] + """ + if model is None: + return "Model not loaded", 0.0 + + # This is an example; you'll need to format the data exactly as your model was trained + # prediction = model.predict([student_data]) + # probability = model.predict_proba([student_data]) + + # Returning mock data for now + risk_level = "High" if student_data[0] < 75 else "Low" + risk_score = 0.85 if risk_level == "High" else 0.25 + + return risk_level, risk_score + +def generate_quiz_questions(topic, level='hard'): + """ + Calls your quiz_generator_v3.py logic. + """ + # Import or call your quiz generator function here + print(f"Generating {level} quiz for topic: {topic}") + # Returning mock data + return [ + { + "question": f"What is the capital of {topic}?", + "options": ["Option A", "Option B", "Option C", "Correct Answer"], + "correct_answer": "Correct Answer" + } + ] + +def get_chatbot_response(user_query, history=None): + """ + Calls your chatbot_v2.py logic. + """ + # Import or call your chatbot function here + print(f"Chatbot processing query: {user_query}") + # Returning a simple response + if "schedule" in user_query.lower(): + return "Your class schedule can be found on the student portal under 'My Schedule'." + else: + return "I am a helpful assistant. How can I help you with your academic questions today?" \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/chatbot_v2.py b/submissions/T039_BlueScreeners/chatbot_v2.py new file mode 100644 index 00000000..9686f89b --- /dev/null +++ b/submissions/T039_BlueScreeners/chatbot_v2.py @@ -0,0 +1,46 @@ +from transformers import pipeline + +def start_chat_v2(): + """ + Initializes and runs a conversational chatbot. + This version manually manages the chat history to avoid import issues. + """ + print("๐Ÿค– Initializing chatbot... This may take a moment.") + chatbot = pipeline("conversational", model="microsoft/DialoGPT-medium") + print("โœ… Chatbot is ready! Type 'exit' to end the conversation.") + print("-" * 30) + + # We will use simple lists to keep track of the conversation history + chat_history = { + "past_user_inputs": [], + "generated_responses": [] + } + + while True: + user_input = input(">> You: ") + + if user_input.lower() in ["exit", "quit"]: + print("๐Ÿ‘‹ Goodbye!") + break + + # Manually build the conversation object the pipeline expects + conversation = { + "past_user_inputs": chat_history["past_user_inputs"], + "generated_responses": chat_history["generated_responses"], + "text": user_input + } + + # Get the response + result = chatbot(conversation) + + # Print the latest response from the bot + bot_response = result["generated_responses"][-1] + print(f">> Bot: {bot_response}") + + # Update our history with the latest turn + chat_history["past_user_inputs"].append(user_input) + chat_history["generated_responses"].append(bot_response) + +# --- Start the Chatbot --- +if __name__ == "__main__": + start_chat_v2() \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/encode_faces.py b/submissions/T039_BlueScreeners/encode_faces.py new file mode 100644 index 00000000..a86f842f --- /dev/null +++ b/submissions/T039_BlueScreeners/encode_faces.py @@ -0,0 +1,45 @@ +# encode_faces.py (Bypassing OpenCV) +import face_recognition +import pickle +import os + +# Path to the directory where student images are stored +folderPath = 'student_images' +pathList = os.listdir(folderPath) +print(f"Found files: {pathList}") + +encodeListKnown = [] +studentUsernames = [] + +print("\nStarting encoding process using the direct loader...") +for path in pathList: + username = os.path.splitext(path)[0] + try: + # Load the image file directly using face_recognition's built-in loader + # This function handles all the necessary conversions automatically + print(f"Processing image for '{username}'...") + image = face_recognition.load_image_file(os.path.join(folderPath, path)) + + # Get face encodings from the image + encodings = face_recognition.face_encodings(image) + + if encodings: + # Use the first face found in the image + encodeListKnown.append(encodings[0]) + studentUsernames.append(username) + print(f"--> Successfully encoded face for: '{username}'") + else: + print(f"Warning: No face was found in the image for '{username}'. Skipping.") + + except Exception as e: + print(f"!!! An unexpected error occurred while processing '{username}': {e}") + +# Save the data only if at least one face was successfully encoded +if encodeListKnown: + encodeData = [studentUsernames, encodeListKnown] + + with open('EncodeFile.p', 'wb') as file: + pickle.dump(encodeData, file) + print("\nEncoding Complete. Face data saved to EncodeFile.p") +else: + print("\nEncoding failed. No faces were successfully encoded.") \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/import_data.py b/submissions/T039_BlueScreeners/import_data.py new file mode 100644 index 00000000..9f61efa8 --- /dev/null +++ b/submissions/T039_BlueScreeners/import_data.py @@ -0,0 +1,39 @@ +# import_data.py +import csv +from server import app, db, User, Student, Teacher # Imports from the configured server file +from werkzeug.security import generate_password_hash + +def import_csv_data(): + with app.app_context(): + print("Dropping all tables...") + db.drop_all() + print("Creating all tables...") + db.create_all() + + with open('student_data.csv', newline='') as csvfile: + reader = csv.DictReader(csvfile) + print("Starting data import...") + for row in reader: + if User.query.filter_by(username=row['username']).first(): + print(f"User '{row['username']}' already exists. Skipping.") + continue + + hashed_password = generate_password_hash(row['password']) + new_user = User(username=row['username'], password_hash=hashed_password, role=row['role']) + db.session.add(new_user) + db.session.commit() + + if new_user.role == 'student': + new_profile = Student(full_name=row['full_name'], user_id=new_user.id) + db.session.add(new_profile) + elif new_user.role == 'teacher': + new_profile = Teacher(full_name=row['full_name'], user_id=new_user.id) + db.session.add(new_profile) + + print(f"Added user: {new_user.username} (Role: {new_user.role})") + + db.session.commit() + print("\nData import complete!") + +if __name__ == '__main__': + import_csv_data() \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/ml_models/__pycache__/chatbot_v2.cpython-39.pyc b/submissions/T039_BlueScreeners/ml_models/__pycache__/chatbot_v2.cpython-39.pyc new file mode 100644 index 00000000..f99eaa67 Binary files /dev/null and b/submissions/T039_BlueScreeners/ml_models/__pycache__/chatbot_v2.cpython-39.pyc differ diff --git a/submissions/T039_BlueScreeners/ml_models/__pycache__/chatbot_v2_web.cpython-39.pyc b/submissions/T039_BlueScreeners/ml_models/__pycache__/chatbot_v2_web.cpython-39.pyc new file mode 100644 index 00000000..bf441be9 Binary files /dev/null and b/submissions/T039_BlueScreeners/ml_models/__pycache__/chatbot_v2_web.cpython-39.pyc differ diff --git a/submissions/T039_BlueScreeners/ml_models/__pycache__/quiz_generator_v3.cpython-39.pyc b/submissions/T039_BlueScreeners/ml_models/__pycache__/quiz_generator_v3.cpython-39.pyc new file mode 100644 index 00000000..727be2a3 Binary files /dev/null and b/submissions/T039_BlueScreeners/ml_models/__pycache__/quiz_generator_v3.cpython-39.pyc differ diff --git a/submissions/T039_BlueScreeners/ml_models/chatbot_v2.py b/submissions/T039_BlueScreeners/ml_models/chatbot_v2.py new file mode 100644 index 00000000..9686f89b --- /dev/null +++ b/submissions/T039_BlueScreeners/ml_models/chatbot_v2.py @@ -0,0 +1,46 @@ +from transformers import pipeline + +def start_chat_v2(): + """ + Initializes and runs a conversational chatbot. + This version manually manages the chat history to avoid import issues. + """ + print("๐Ÿค– Initializing chatbot... This may take a moment.") + chatbot = pipeline("conversational", model="microsoft/DialoGPT-medium") + print("โœ… Chatbot is ready! Type 'exit' to end the conversation.") + print("-" * 30) + + # We will use simple lists to keep track of the conversation history + chat_history = { + "past_user_inputs": [], + "generated_responses": [] + } + + while True: + user_input = input(">> You: ") + + if user_input.lower() in ["exit", "quit"]: + print("๐Ÿ‘‹ Goodbye!") + break + + # Manually build the conversation object the pipeline expects + conversation = { + "past_user_inputs": chat_history["past_user_inputs"], + "generated_responses": chat_history["generated_responses"], + "text": user_input + } + + # Get the response + result = chatbot(conversation) + + # Print the latest response from the bot + bot_response = result["generated_responses"][-1] + print(f">> Bot: {bot_response}") + + # Update our history with the latest turn + chat_history["past_user_inputs"].append(user_input) + chat_history["generated_responses"].append(bot_response) + +# --- Start the Chatbot --- +if __name__ == "__main__": + start_chat_v2() \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/ml_models/chatbot_v2_web.py b/submissions/T039_BlueScreeners/ml_models/chatbot_v2_web.py new file mode 100644 index 00000000..80027632 --- /dev/null +++ b/submissions/T039_BlueScreeners/ml_models/chatbot_v2_web.py @@ -0,0 +1,27 @@ +from transformers import pipeline + +class WebChatbot: + def __init__(self): + """ + Initializes the text-generation chatbot pipeline and chat history. + """ + self.chatbot = pipeline("text-generation", model="microsoft/DialoGPT-medium") + self.chat_history = [] + + def get_response(self, user_input): + """ + Accepts user input string, returns bot response string and updates chat history. + """ + # Append user input to chat history + self.chat_history.append(user_input) + + # Generate response using text-generation pipeline + # Join chat history as context + context = " ".join(self.chat_history[-5:]) # last 5 messages as context + outputs = self.chatbot(context, max_length=100, num_return_sequences=1) + bot_response = outputs[0]['generated_text'][len(context):].strip() + + # Append bot response to chat history + self.chat_history.append(bot_response) + + return bot_response, self.chat_history diff --git a/submissions/T039_BlueScreeners/ml_models/quiz_generator_v3.py b/submissions/T039_BlueScreeners/ml_models/quiz_generator_v3.py new file mode 100644 index 00000000..7c0f0cd7 --- /dev/null +++ b/submissions/T039_BlueScreeners/ml_models/quiz_generator_v3.py @@ -0,0 +1,167 @@ +import spacy +from transformers import pipeline +import random +from chatbot_v2 import start_chat_v2 # Integrate chatbot + +# Load a spaCy model to help find key phrases (potential answers) +nlp = spacy.load("en_core_web_sm") + +# Use a smaller or CPU-optimized model for fill-mask to reduce memory usage +question_generator = pipeline("text2text-generation", model="valhalla/t5-small-qg-hl") +mask_filler = pipeline("fill-mask", model="distilbert-base-uncased") + +# Mock user data for personalization +user_data = { + "user1": {"struggles": ["math", "physics"], "level": "beginner"}, + "user2": {"struggles": ["biology"], "level": "intermediate"}, +} + +# Weekly quiz subjects +weekly_subjects = ["math", "physics", "chemistry", "biology", "history", "literature"] + +# Points for weekly quiz +weekly_points = {1: 1000, 2: 750, 3: 500} + +def generate_quiz_v3(context: str) -> list: + """ + Generates a multiple-choice quiz using a multi-pipeline approach. + 1. Extracts a potential answer. + 2. Generates a question for that answer. + 3. Generates distractors using a fill-mask model. + """ + print("๐Ÿค– Analyzing text to find a key phrase for the answer...") + + # --- Step 1: Find a potential answer in the text --- + doc = nlp(context) + # Extract noun chunks or entities as potential answers + potential_answers = [chunk.text for chunk in doc.noun_chunks if len(chunk.text.split()) > 1] + if not potential_answers: + print("Could not find suitable key phrases to use as an answer.") + return [] + + # Pick a random key phrase as our correct answer + correct_answer = random.choice(potential_answers) + print(f"โœ… Selected Answer: '{correct_answer}'") + + # Find the sentence this answer came from + input_sentence = "" + for sent in doc.sents: + if correct_answer in sent.text: + input_sentence = sent.text + break + if not input_sentence: + return [] # Should not happen if answer was found + + # --- Step 2: Generate a question for that answer --- + print("๐Ÿค– Generating a question for the selected answer...") + # The model expects the answer to be highlighted with tags + qg_input = f" {correct_answer} {input_sentence}" + generated_q = question_generator(qg_input, max_length=64) + question = generated_q[0]['generated_text'] + print(f"โœ… Generated Question: '{question}'") + + # --- Step 3: Generate distractors (incorrect options) --- + print("๐Ÿค– Generating incorrect options (distractors)...") + # Replace the answer in the sentence with a token + masked_sentence = input_sentence.replace(correct_answer, mask_filler.tokenizer.mask_token) + + # Use the fill-mask model to predict words for the blank + predictions = mask_filler(masked_sentence) + + # Filter predictions to get unique and different options + distractors = [] + for pred in predictions: + # Make sure the predicted token is not part of the correct answer + if pred['token_str'].lower() not in correct_answer.lower(): + distractors.append(pred['token_str']) + + # We need 3 distractors + if len(distractors) < 3: + # Fallback if we don't get enough unique distractors + distractors.extend(["none of the above", "all of the above", "a different process"]) + + distractors = list(set(distractors))[:3] # Get 3 unique distractors + + # --- Final Assembly --- + options = [correct_answer] + distractors + random.shuffle(options) # Mix up the options + + quiz_item = { + "question": question, + "options": options, + "correct_answer": correct_answer + } + + return [quiz_item] + +def generate_personalized_quiz(user_id: str, context: str) -> list: + """ + Generates a personalized quiz based on user's academic struggles. + """ + if user_id not in user_data: + print("User not found, generating general quiz.") + return generate_quiz_v3(context) + + struggles = user_data[user_id]["struggles"] + level = user_data[user_id]["level"] + + # Adjust context based on struggles (mock: if struggles include 'math', focus on math) + if "math" in struggles: + context = "Mathematics involves numbers, shapes, and patterns. Algebra is a branch of mathematics dealing with symbols and rules for manipulating them." + # Similarly for others + + quiz = generate_quiz_v3(context) + # Adjust difficulty based on level + if level == "beginner": + # Make easier, perhaps fewer options or simpler + pass + return quiz + +def generate_weekly_quiz() -> list: + """ + Generates a hard weekly quiz covering all subjects. + """ + quiz = [] + for subject in weekly_subjects: + # Mock context for each subject + context = f"{subject.capitalize()} is an important subject. It covers various topics essential for understanding the world." + subject_quiz = generate_quiz_v3(context) + if subject_quiz: + quiz.extend(subject_quiz) + return quiz + +def assign_weekly_points(team_rankings: list) -> dict: + """ + Assigns points to teams based on rankings. + team_rankings: list of team names in order + """ + points = {} + for i, team in enumerate(team_rankings[:3]): + points[team] = weekly_points.get(i+1, 0) + return points + +def start_integrated_chatbot(): + """ + Starts the integrated chatbot. + """ + start_chat_v2() + + +# --- Example Usage --- +if __name__ == "__main__": + educational_text = """ + Photosynthesis is a crucial process used by plants, algae, and some bacteria to convert light energy into + chemical energy, through a process that converts carbon dioxide and water into glucose (a sugar) and oxygen. + This process is essential for life on Earth as it produces most of the planet's oxygen and serves as the + primary source of energy for most ecosystems. + """ + + quiz = generate_quiz_v3(educational_text) + + if quiz: + question_data = quiz[0] + print("\n--- Generated Quiz (Robust Version) ---") + print(f"Q: {question_data['question']}") + for i, option in enumerate(question_data['options']): + print(f" {chr(65+i)}) {option}") + print(f"\nCorrect Answer: {question_data['correct_answer']}") diff --git a/submissions/T039_BlueScreeners/ml_models/trained_model.pkl b/submissions/T039_BlueScreeners/ml_models/trained_model.pkl new file mode 100644 index 00000000..b41634a9 Binary files /dev/null and b/submissions/T039_BlueScreeners/ml_models/trained_model.pkl differ diff --git a/submissions/T039_BlueScreeners/quiz_generator_v3.py b/submissions/T039_BlueScreeners/quiz_generator_v3.py new file mode 100644 index 00000000..f8e2ba19 --- /dev/null +++ b/submissions/T039_BlueScreeners/quiz_generator_v3.py @@ -0,0 +1,102 @@ +import spacy +from transformers import pipeline +import random + +# Load a spaCy model to help find key phrases (potential answers) +nlp = spacy.load("en_core_web_sm") + +# Load the specialized pipelines +question_generator = pipeline("text2text-generation", model="valhalla/t5-base-qg-hl") +mask_filler = pipeline("fill-mask", model="bert-base-uncased") + +def generate_quiz_v3(context: str) -> list: + """ + Generates a multiple-choice quiz using a multi-pipeline approach. + 1. Extracts a potential answer. + 2. Generates a question for that answer. + 3. Generates distractors using a fill-mask model. + """ + print("๐Ÿค– Analyzing text to find a key phrase for the answer...") + + # --- Step 1: Find a potential answer in the text --- + doc = nlp(context) + # Extract noun chunks or entities as potential answers + potential_answers = [chunk.text for chunk in doc.noun_chunks if len(chunk.text.split()) > 1] + if not potential_answers: + print("Could not find suitable key phrases to use as an answer.") + return [] + + # Pick a random key phrase as our correct answer + correct_answer = random.choice(potential_answers) + print(f"โœ… Selected Answer: '{correct_answer}'") + + # Find the sentence this answer came from + input_sentence = "" + for sent in doc.sents: + if correct_answer in sent.text: + input_sentence = sent.text + break + if not input_sentence: + return [] # Should not happen if answer was found + + # --- Step 2: Generate a question for that answer --- + print("๐Ÿค– Generating a question for the selected answer...") + # The model expects the answer to be highlighted with tags + qg_input = f" {correct_answer} {input_sentence}" + generated_q = question_generator(qg_input, max_length=64) + question = generated_q[0]['generated_text'] + print(f"โœ… Generated Question: '{question}'") + + # --- Step 3: Generate distractors (incorrect options) --- + print("๐Ÿค– Generating incorrect options (distractors)...") + # Replace the answer in the sentence with a token + masked_sentence = input_sentence.replace(correct_answer, mask_filler.tokenizer.mask_token) + + # Use the fill-mask model to predict words for the blank + predictions = mask_filler(masked_sentence) + + # Filter predictions to get unique and different options + distractors = [] + for pred in predictions: + # Make sure the predicted token is not part of the correct answer + if pred['token_str'].lower() not in correct_answer.lower(): + distractors.append(pred['token_str']) + + # We need 3 distractors + if len(distractors) < 3: + # Fallback if we don't get enough unique distractors + distractors.extend(["none of the above", "all of the above", "a different process"]) + + distractors = list(set(distractors))[:3] # Get 3 unique distractors + + # --- Final Assembly --- + options = [correct_answer] + distractors + random.shuffle(options) # Mix up the options + + quiz_item = { + "question": question, + "options": options, + "correct_answer": correct_answer + } + + return [quiz_item] + + +# --- Example Usage --- +if __name__ == "__main__": + educational_text = """ + Photosynthesis is a crucial process used by plants, algae, and some bacteria to convert light energy into + chemical energy, through a process that converts carbon dioxide and water into glucose (a sugar) and oxygen. + This process is essential for life on Earth as it produces most of the planet's oxygen and serves as the + primary source of energy for most ecosystems. + """ + + quiz = generate_quiz_v3(educational_text) + + if quiz: + question_data = quiz[0] + print("\n--- Generated Quiz (Robust Version) ---") + print(f"Q: {question_data['question']}") + for i, option in enumerate(question_data['options']): + print(f" {chr(65+i)}) {option}") + print(f"\nCorrect Answer: {question_data['correct_answer']}") \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/requirements.txt b/submissions/T039_BlueScreeners/requirements.txt new file mode 100644 index 00000000..f9d56c61 --- /dev/null +++ b/submissions/T039_BlueScreeners/requirements.txt @@ -0,0 +1,8 @@ +Flask +Flask-SQLAlchemy +mysql-connector-python +Flask-Login +opencv-python +face_recognition +numpy +python-dotenv \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/server.py b/submissions/T039_BlueScreeners/server.py new file mode 100644 index 00000000..6de4dbae --- /dev/null +++ b/submissions/T039_BlueScreeners/server.py @@ -0,0 +1,477 @@ +import os +from flask import Flask, render_template, request, redirect, url_for, flash +from flask_sqlalchemy import SQLAlchemy +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user +from werkzeug.security import generate_password_hash, check_password_hash +from functools import wraps +from datetime import date, datetime, timedelta +from ml_models.quiz_generator_v3 import generate_personalized_quiz, generate_weekly_quiz, assign_weekly_points, start_integrated_chatbot + +from flask import session # Added import for session + +import threading +import time + +app = Flask(__name__) +app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+mysqlconnector://root:2580@127.0.0.1/student_platform_db" +app.config['SECRET_KEY'] = "a-very-strong-secret-key-for-this-hackathon" +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + +db = SQLAlchemy(app) + +with app.app_context(): + db.create_all() +login_manager = LoginManager(app) +login_manager.login_view = 'login' + +# Track server start time for logout all users on restart +server_start_time = time.time() + +# Background thread to stop attendance sessions after 3 minutes +def attendance_session_watcher(): + while True: + time.sleep(30) # check every 30 seconds + with app.app_context(): + active_sessions = AttendanceSession.query.filter_by(is_active=True).all() + now = datetime.utcnow() + for session in active_sessions: + if session.start_time and (now - session.start_time).total_seconds() >= 180: + session.is_active = False + db.session.commit() + +# Start the background thread +threading.Thread(target=attendance_session_watcher, daemon=True).start() + +# --- New Model for Weekly Quiz Activation --- +class WeeklyQuizStatus(db.Model): + __tablename__ = 'weekly_quiz_status' + id = db.Column(db.Integer, primary_key=True) + is_active = db.Column(db.Boolean, default=False) + +def role_required(role): + def decorator(f): + @wraps(f) + def decorated_function(*args, **kwargs): + if not current_user.is_authenticated or current_user.role != role: + flash(f"Access for {role}s only.", "danger") + return redirect(url_for('login')) + return f(*args, **kwargs) + return decorated_function + return decorator + +# --- Routes for Teacher to Activate/Deactivate Weekly Quiz --- +@app.route('/teacher/weekly_quiz_status', methods=['GET', 'POST']) +@login_required +@role_required('teacher') +def weekly_quiz_status(): + status = WeeklyQuizStatus.query.first() + if request.method == 'POST': + action = request.form.get('action') + if not status: + status = WeeklyQuizStatus(is_active=False) + db.session.add(status) + if action == 'activate': + status.is_active = True + flash('Weekly quiz activated.', 'success') + elif action == 'deactivate': + status.is_active = False + flash('Weekly quiz deactivated.', 'info') + db.session.commit() + return redirect(url_for('weekly_quiz_status')) + return render_template('weekly_quiz_status.html', status=status) + +# --- Modify Weekly Quiz Route to Check Activation --- +@app.route('/quiz/weekly') +@login_required +def weekly_quiz(): + status = WeeklyQuizStatus.query.first() + if not status or not status.is_active: + flash('Weekly quiz is not active currently.', 'warning') + return redirect(url_for('dashboard')) + quiz = generate_weekly_quiz() + return render_template('quiz.html', quiz=quiz) + +# --- Leaderboard Route --- +@app.route('/leaderboard') +@login_required +def leaderboard(): + # Mock implementation: get top 3 users by weekly quiz scores + # In real app, query database for actual scores + top_users = [ + {'username': 'user1', 'score': 95, 'reward': 1000}, + {'username': 'user2', 'score': 90, 'reward': 750}, + {'username': 'user3', 'score': 85, 'reward': 500}, + ] + return render_template('leaderboard.html', top_users=top_users) + +# --- Academic Analysis Route --- +@app.route('/academic_analysis') +@login_required +@role_required('student') +def academic_analysis(): + # Mock data for charts + attendance_data = {'Present': 80, 'Absent': 20} + quiz_scores = [70, 75, 80, 85, 90] + academic_marks = [65, 70, 75, 80, 85] + return render_template('academic_analysis.html', + attendance_data=attendance_data, + quiz_scores=quiz_scores, + academic_marks=academic_marks) + +# Before request handler to logout users if server restarted after their login +@app.before_request +def check_session_validity(): + if current_user.is_authenticated: + # Flask session does not store login time by default, so we store it in session on login + login_time = session.get('login_time') + if login_time is None: + # Set login time if not set + session['login_time'] = time.time() + else: + if login_time < server_start_time: + logout_user() + flash('You have been logged out due to server restart. Please login again.', 'info') + return redirect(url_for('login')) + +# --- Models --- +class User(UserMixin, db.Model): + __tablename__ = 'users' + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + password_hash = db.Column(db.String(256), nullable=False) + role = db.Column(db.String(20), nullable=False) + +class Student(db.Model): + __tablename__ = 'students' + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey('users.id'), unique=True, nullable=False) + full_name = db.Column(db.String(120), nullable=False) + user = db.relationship('User', backref=db.backref('student', uselist=False)) + +class Teacher(db.Model): + __tablename__ = 'teachers' + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey('users.id'), unique=True, nullable=False) + full_name = db.Column(db.String(120), nullable=False) + user = db.relationship('User', backref=db.backref('teacher', uselist=False)) + +class AttendanceRecord(db.Model): + __tablename__ = 'attendance_records' + id = db.Column(db.Integer, primary_key=True) + student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False) + date = db.Column(db.Date, nullable=False) + status = db.Column(db.String(10), nullable=False) + +class AttendanceSession(db.Model): + __tablename__ = 'attendance_sessions' + id = db.Column(db.Integer, primary_key=True) + teacher_id = db.Column(db.Integer, db.ForeignKey('teachers.id'), nullable=False) + start_time = db.Column(db.DateTime, server_default=db.func.now()) + is_active = db.Column(db.Boolean, default=True) + +class Complaint(db.Model): + __tablename__ = 'complaints' + id = db.Column(db.Integer, primary_key=True) + message = db.Column(db.Text, nullable=False) + submitted_at = db.Column(db.DateTime, default=datetime.utcnow) + +class Assignment(db.Model): + __tablename__ = 'assignments' + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(200), nullable=False) + teacher_id = db.Column(db.Integer, db.ForeignKey('teachers.id'), nullable=False) + +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + +# --- Main & Authentication Routes --- +@app.route('/') +def home(): + return render_template('home.html', current_user=current_user) + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if current_user.is_authenticated: return redirect(url_for('dashboard')) + if request.method == 'POST': + user = User.query.filter_by(username=request.form.get('username')).first() + if user and check_password_hash(user.password_hash, request.form.get('password')): + login_user(user) + session['login_time'] = time.time() # Set login time in session on successful login + return redirect(url_for('dashboard')) + flash('Invalid credentials.') + return render_template('login.html') + +@app.route('/register', methods=['GET', 'POST']) +def register(): + if request.method == 'POST': + username = request.form.get('username') + full_name = request.form.get('full_name') + password = request.form.get('password') + role = request.form.get('role') + + if User.query.filter_by(username=username).first(): + flash('Username already exists.') + return redirect(url_for('register')) + + hashed_password = generate_password_hash(password) + new_user = User(username=username, password_hash=hashed_password, role=role) + db.session.add(new_user) + db.session.commit() + + if role == 'student': + new_profile = Student(full_name=full_name, user_id=new_user.id) + elif role == 'teacher': + new_profile = Teacher(full_name=full_name, user_id=new_user.id) + else: + new_profile = None + + if new_profile: + db.session.add(new_profile) + db.session.commit() + + flash('Registration successful! Please log in.') + return redirect(url_for('login')) + return render_template('register.html') + +@app.route('/logout') +@login_required +def logout(): + logout_user() + return redirect(url_for('home')) + +# --- Dashboard Redirector --- +@app.route('/dashboard') +@login_required +def dashboard(): + if current_user.role == 'admin': return redirect(url_for('admin_dashboard')) + if current_user.role == 'teacher': return redirect(url_for('teacher_dashboard')) + if current_user.role == 'student': return redirect(url_for('student_dashboard')) + return redirect(url_for('login')) + +# --- Admin Routes --- +@app.route('/admin/dashboard') +@login_required +@role_required('admin') +def admin_dashboard(): + # In a real app, you'd query for this data + at_risk_students = [] + all_students = [] + return render_template('admin_dashboard.html', at_risk_students=at_risk_students, all_students=all_students) + +@app.route('/add_user', methods=['POST']) +@login_required +@role_required('admin') +def add_user(): + full_name = request.form.get('full_name') + username = request.form.get('username') + password = request.form.get('password') + role = request.form.get('role') + + if User.query.filter_by(username=username).first(): + flash('Username already exists.') + return redirect(url_for('admin_dashboard')) + + hashed_password = generate_password_hash(password) + new_user = User(username=username, password_hash=hashed_password, role=role) + db.session.add(new_user) + db.session.commit() + + if role == 'student': + new_profile = Student(full_name=full_name, user_id=new_user.id) + elif role == 'teacher': + new_profile = Teacher(full_name=full_name, user_id=new_user.id) + else: + new_profile = None + + if new_profile: + db.session.add(new_profile) + db.session.commit() + + flash('User added successfully.') + return redirect(url_for('admin_dashboard')) + +@app.route('/admin/complaints') +@login_required +@role_required('admin') +def view_complaints(): + complaints = Complaint.query.order_by(Complaint.submitted_at.desc()).all() + return render_template('complaints.html', complaints=complaints) + +# --- Teacher Routes --- +@app.route('/teacher/dashboard') +@login_required +@role_required('teacher') +def teacher_dashboard(): + assignments = Assignment.query.filter_by(teacher_id=current_user.teacher.id).all() + active_session = AttendanceSession.query.filter_by(teacher_id=current_user.teacher.id, is_active=True).first() + return render_template('teacher_dashboard.html', active_session=active_session, assignments=assignments) + +@app.route('/start_attendance_session', methods=['POST']) +@login_required +@role_required('teacher') +def start_attendance_session(): + existing = AttendanceSession.query.filter_by(teacher_id=current_user.teacher.id, is_active=True).first() + if existing: + flash('Session already active.') + return redirect(url_for('teacher_dashboard')) + new_session = AttendanceSession(teacher_id=current_user.teacher.id) + db.session.add(new_session) + db.session.commit() + flash('Attendance session started.') + return redirect(url_for('teacher_dashboard')) + +@app.route('/stop_attendance_session', methods=['POST']) +@login_required +@role_required('teacher') +def stop_attendance_session(): + session = AttendanceSession.query.filter_by(teacher_id=current_user.teacher.id, is_active=True).first() + if session: + session.is_active = False + db.session.commit() + flash('Session stopped.') + return redirect(url_for('teacher_dashboard')) + +@app.route('/create_assignment', methods=['POST']) +@login_required +@role_required('teacher') +def create_assignment(): + title = request.form.get('title') + if title: + new_assignment = Assignment(title=title, teacher_id=current_user.teacher.id) + db.session.add(new_assignment) + db.session.commit() + flash('Assignment created.') + return redirect(url_for('teacher_dashboard')) + +@app.route('/view_submissions/') +@login_required +@role_required('teacher') +def view_submissions(assignment_id): + assignment = Assignment.query.get_or_404(assignment_id) + if assignment.teacher_id != current_user.teacher.id: + flash('Access denied.') + return redirect(url_for('teacher_dashboard')) + # Since no submissions model, pass empty list + submissions = [] + return render_template('view_submission.html', assignment=assignment, submissions=submissions) + +# --- Student Routes --- +@app.route('/student/dashboard') +@login_required +@role_required('student') +def student_dashboard(): + return render_template('student_dashboard.html') + +@app.route('/student/attendance') +@login_required +@role_required('student') +def student_attendance_page(): + # Check if there is any active attendance session + active_session = AttendanceSession.query.filter_by(is_active=True).first() + if not active_session: + flash('Attendance session is not active currently. Please wait for your teacher to start the session.', 'warning') + return redirect(url_for('student_dashboard')) + return render_template('attendance.html') + +@app.route('/api_mark_attendance', methods=['POST']) +@login_required +@role_required('student') +def api_mark_attendance(): + from flask import jsonify + import base64 + import io + from PIL import Image + import numpy as np + + # Check if attendance session is active + active_session = AttendanceSession.query.filter_by(is_active=True).first() + if not active_session: + return jsonify({'status': 'error', 'message': 'Attendance session is not active. Cannot mark attendance.'}) + + data = request.get_json() + image_data = data.get('image', None) + if not image_data: + return jsonify({'status': 'error', 'message': 'No image data provided.'}) + + # Remove the data URL prefix + header, encoded = image_data.split(',', 1) + image_bytes = base64.b64decode(encoded) + + # Load image with PIL + image = Image.open(io.BytesIO(image_bytes)) + + # Here you would call your face recognition logic to identify students + # For now, we mock the recognized names as the logged in user + recognized_names = [current_user.username] + + # Mark attendance logic (mock) + # In real app, you would update AttendanceRecord for recognized students + + return jsonify({'status': 'success', 'names': recognized_names}) + +@app.route('/student/complaint', methods=['GET', 'POST']) +@login_required +@role_required('student') +def submit_complaint(): + if request.method == 'POST': + message = request.form.get('message') + if message: + new_complaint = Complaint(message=message) + db.session.add(new_complaint) + db.session.commit() + flash('Your anonymous suggestion has been submitted.', 'success') + return redirect(url_for('student_dashboard')) + return render_template('submit_complaint.html') + +# --- Quiz Routes --- +@app.route('/quiz/personalized') +@login_required +def personalized_quiz(): + user_id = current_user.username + context = request.args.get('context', "General educational content about science and math.") + quiz = generate_personalized_quiz(user_id, context) + return render_template('quiz.html', quiz=quiz) + +@app.route('/quiz/points') +@login_required +def quiz_points(): + # Mock team rankings + team_rankings = ["Team A", "Team B", "Team C"] + points = assign_weekly_points(team_rankings) + return render_template('points.html', points=points) + +from ml_models.chatbot_v2_web import WebChatbot +from flask import jsonify + +chatbot_instance = WebChatbot() + +@app.route('/chatbot') +@login_required +def chatbot(): + return render_template('chatbot.html') + +@app.route('/chatbot_message', methods=['POST']) +@login_required +def chatbot_message(): + data = request.get_json() + user_message = data.get('message', '') + if not user_message: + return jsonify({'error': 'No message provided'}), 400 + + bot_response, chat_history = chatbot_instance.get_response(user_message) + return jsonify({ + 'response': bot_response, + 'chat_history': chat_history + }) + +import sys + +if __name__ == '__main__': + port = 5000 + if len(sys.argv) > 1: + try: + port = int(sys.argv[1]) + except ValueError: + pass + app.run(host='0.0.0.0', port=port, debug=True) diff --git a/submissions/T039_BlueScreeners/student_data.csv b/submissions/T039_BlueScreeners/student_data.csv new file mode 100644 index 00000000..029d6d56 --- /dev/null +++ b/submissions/T039_BlueScreeners/student_data.csv @@ -0,0 +1,4 @@ +username,password,full_name,role +admin,admin123,Admin User,admin +teacher1,pass123,Dr. Singh,teacher +sarbjot,2580,Sarbjot Kumar,student \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/tempCodeRunnerFile.py b/submissions/T039_BlueScreeners/tempCodeRunnerFile.py new file mode 100644 index 00000000..27cae99f --- /dev/null +++ b/submissions/T039_BlueScreeners/tempCodeRunnerFile.py @@ -0,0 +1,191 @@ +import os +from flask import Flask, render_template, request, redirect, url_for, flash +from flask_sqlalchemy import SQLAlchemy +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user +from werkzeug.security import generate_password_hash, check_password_hash +from functools import wraps +from datetime import date, datetime, timedelta + +# Make sure you have the ai_services.py file with placeholder functions +# from ai_services import load_prediction_model, get_risk_prediction + +# --- 1. App Initialization & Configuration --- +app = Flask(__name__) +app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+mysqlconnector://root:2580@127.0.0.1/student_platform_db" +app.config['SECRET_KEY'] = "a-very-strong-secret-key-for-this-hackathon" +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + +db = SQLAlchemy(app) +login_manager = LoginManager(app) +login_manager.login_view = 'login' + +# --- 2. Database Models --- +class User(UserMixin, db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + password_hash = db.Column(db.String(256), nullable=False) + role = db.Column(db.String(20), nullable=False) # 'admin', 'teacher', 'student' + +class Student(db.Model): + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + full_name = db.Column(db.String(120), nullable=False) + user = db.relationship('User', backref='student', uselist=False) + attendance = db.relationship('AttendanceRecord', backref='student', lazy='dynamic') + +class Teacher(db.Model): + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + full_name = db.Column(db.String(120), nullable=False) + user = db.relationship('User', backref='teacher', uselist=False) + +class AttendanceRecord(db.Model): + id = db.Column(db.Integer, primary_key=True) + student_id = db.Column(db.Integer, db.ForeignKey('student.id'), nullable=False) + date = db.Column(db.Date, nullable=False) + status = db.Column(db.String(10), nullable=False) + +class AttendanceSession(db.Model): + id = db.Column(db.Integer, primary_key=True) + teacher_id = db.Column(db.Integer, db.ForeignKey('teacher.id'), nullable=False) + start_time = db.Column(db.DateTime, server_default=db.func.now()) + is_active = db.Column(db.Boolean, default=True) + +class Complaint(db.Model): + id = db.Column(db.Integer, primary_key=True) + message = db.Column(db.Text, nullable=False) + +# --- 3. User Loader & Decorators --- +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + +def role_required(role): + def decorator(f): + @wraps(f) + def decorated_function(*args, **kwargs): + if not current_user.is_authenticated or current_user.role != role: + flash(f"Access for {role}s only.", "danger") + return redirect(url_for('login')) + return f(*args, **kwargs) + return decorated_function + return decorator + +# --- 4. Main & Authentication Routes --- +@app.route('/') +def home(): + # MODIFICATION: Pass current_user to the home template + return render_template('home.html', current_user=current_user) + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if current_user.is_authenticated: + return redirect(url_for('dashboard')) + if request.method == 'POST': + user = User.query.filter_by(username=request.form.get('username')).first() + if user and check_password_hash(user.password_hash, request.form.get('password')): + login_user(user) + return redirect(url_for('dashboard')) + flash('Invalid credentials.') + return render_template('login.html') + +@app.route('/register', methods=['GET', 'POST']) +def register(): + # This route is added back to prevent BuildErrors from the login page toggle + if request.method == 'POST': + username = request.form.get('username') + role = request.form.get('role') + # For a real app, you would add full_name and create a student/teacher profile + if User.query.filter_by(username=username).first(): + flash('Username already exists.') + return redirect(url_for('register')) + hashed_password = generate_password_hash(request.form.get('password')) + new_user = User(username=username, password_hash=hashed_password, role=role) + db.session.add(new_user) + db.session.commit() + flash('Registration successful! Please log in.') + return redirect(url_for('login')) + return render_template('register.html') + +@app.route('/logout') +@login_required +def logout(): + logout_user() + return redirect(url_for('home')) + +# --- 5. Dashboard Redirector --- +@app.route('/dashboard') +@login_required +def dashboard(): + if current_user.role == 'admin': return redirect(url_for('admin_dashboard')) + if current_user.role == 'teacher': return redirect(url_for('teacher_dashboard')) + if current_user.role == 'student': return redirect(url_for('student_dashboard')) + return redirect(url_for('login')) + +# --- 6. Admin Routes --- +@app.route('/admin/dashboard') +@login_required +@role_required('admin') +def admin_dashboard(): + return render_template('admin_dashboard.html') + +@app.route('/admin/add_user', methods=['POST']) +@login_required +@role_required('admin') +def add_user(): + # This function is called by the form in the admin dashboard + # (Your full add_user logic here) + flash('User added successfully (logic to be built).') + return redirect(url_for('admin_dashboard')) + +@app.route('/admin/complaints') +@login_required +@role_required('admin') +def view_complaints(): + complaints = Complaint.query.all() + return render_template('complaints.html', complaints=complaints) + +# --- 7. Teacher Routes --- +@app.route('/teacher/dashboard') +@login_required +@role_required('teacher') +def teacher_dashboard(): + active_session = AttendanceSession.query.filter_by(teacher_id=current_user.teacher.id, is_active=True).first() + return render_template('teacher_dashboard.html', active_session=active_session) + +# (Add other teacher routes like start/stop session here) + +# --- 8. Student Routes --- +@app.route('/student/dashboard') +@login_required +@role_required('student') +def student_dashboard(): + return render_template('student_dashboard.html') + +@app.route('/student/attendance') +@login_required +@role_required('student') +def student_attendance_page(): + # This is the missing function that caused the error + return render_template('attendance.html') + +@app.route('/student/complaint', methods=['GET', 'POST']) +@login_required +@role_required('student') +def submit_complaint(): + if request.method == 'POST': + message = request.form.get('message') + if message: + new_complaint = Complaint(message=message) + db.session.add(new_complaint) + db.session.commit() + flash('Your anonymous suggestion has been submitted.', 'success') + return redirect(url_for('student_dashboard')) + return render_template('submit_complaint.html') + + +# --- 9. Run Application --- +if __name__ == '__main__': + with app.app_context(): + pass + app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/academic_analysis.html b/submissions/T039_BlueScreeners/templates/academic_analysis.html new file mode 100644 index 00000000..30153b2f --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/academic_analysis.html @@ -0,0 +1,161 @@ + + + + + Academic Analysis + + + +

Academic Performance Analysis

+ +

Attendance

+ + +

Quiz Scores Over Time

+ + +

Academic Marks Over Time

+ + + Back to Dashboard + + + type: 'pie', + data: { + labels: Object.keys({{ attendance_data|tojson }}), + datasets: [{ + data: Object.values({{ attendance_data|tojson }}), + backgroundColor: ['#4CAF50', '#F44336'] + }] + }, + options: { + responsive: true, + plugins: { + legend: { position: 'top' }, + title: { display: true, text: 'Attendance Distribution' } + } + } + }); + + // Quiz Scores Line Chart + const quizScoresCtx = document.getElementById('quizScoresChart').getContext('2d'); + const quizScoresChart = new Chart(quizScoresCtx, { + type: 'line', + data: { + labels: {{ quiz_scores|length|range|list }}, + datasets: [{ + label: 'Quiz Scores', + data: {{ quiz_scores|tojson }}, + borderColor: '#2196F3', + fill: false, + tension: 0.1 + }] + }, + options: { + responsive: true, + plugins: { + legend: { position: 'top' }, + title: { display: true, text: 'Quiz Scores Over Time' } + }, + scales: { + y: { beginAtZero: true, max: 100 } + } + } + }); + + // Academic Marks Bar Chart + const academicMarksCtx = document.getElementById('academicMarksChart').getContext('2d'); + const academicMarksChart = new Chart(academicMarksCtx, { + type: 'bar', + data: { + labels: {{ academic_marks|length|range|list }}, + datasets: [{ + label: 'Academic Marks', + data: {{ academic_marks|tojson }}, + backgroundColor: '#FFC107' + }] + }, + options: { + responsive: true, + plugins: { + legend: { position: 'top' }, + title: { display: true, text: 'Academic Marks Over Time' } + }, + scales: { + y: { beginAtZero: true, max: 100 } + } + } + }); + + + diff --git a/submissions/T039_BlueScreeners/templates/admin_dashboard.html b/submissions/T039_BlueScreeners/templates/admin_dashboard.html new file mode 100644 index 00000000..0a0adc59 --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/admin_dashboard.html @@ -0,0 +1,77 @@ +{% extends "base.html" %} +{% block title %}Admin Dashboard{% endblock %} + +{% block content %} + + +
+

Platform Overview

+ +
+

High-Risk Students Highlight

+ {% if at_risk_students %} +
    + {% for student in at_risk_students %} +
  • {{ student.full_name }} - (Risk Score: {{ "%.2f"|format(student.risk_score) }})
  • + {% endfor %} +
+ {% else %} +

No students are currently at high risk.

+ {% endif %} +
+ +
+

Add New User

+
+ + + + + +
+
+ +
+

All Students Analysis

+
+ + + + + + + + + + {% for student in all_students %} + + + + + + {% endfor %} + +
Full NameRisk LevelRisk Score
{{ student.full_name }} + + {{ student.risk_level }} + + {{ "%.2f"|format(student.risk_score) }}
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/attendance.html b/submissions/T039_BlueScreeners/templates/attendance.html new file mode 100644 index 00000000..e20c5cd0 --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/attendance.html @@ -0,0 +1,88 @@ +{% extends "base.html" %} +{% block title %}Mark Attendance{% endblock %} + +{% block content %} +
+

Face Recognition Attendance

+

Position your face in the center of the frame. The system will scan automatically.

+ +
+ + +
+ +
Initializing camera...
+
+ + +{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/base.html b/submissions/T039_BlueScreeners/templates/base.html new file mode 100644 index 00000000..b8481e2c --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/base.html @@ -0,0 +1,70 @@ + + + + + + {% block title %}Student Success Platform{% endblock %} + + + + + + + + + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+ {% for category, message in messages %} + {% if category == 'success' %} +
+ {% elif category == 'danger' %} +
+ {% else %} +
+ {% endif %} + {{ message }} +
+ {% endfor %} +
+ {% endif %} + {% endwith %} + + {% block content %}{% endblock %} + + + + \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/chatbot.html b/submissions/T039_BlueScreeners/templates/chatbot.html new file mode 100644 index 00000000..3bebcb9b --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/chatbot.html @@ -0,0 +1,65 @@ +{% extends "base.html" %} +{% block title %}AI Chatbot{% endblock %} +{% block content %} +
+

AI Chatbot

+
+ +
+
+ + +
+
+ + +{% endblock %} diff --git a/submissions/T039_BlueScreeners/templates/complaints.html b/submissions/T039_BlueScreeners/templates/complaints.html new file mode 100644 index 00000000..b8426032 --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/complaints.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} +{% block title %}Anonymous Complaints{% endblock %} +{% block content %} + +
+

Anonymous Complaints & Suggestions

+ + {% if complaints %} +
+ {% for complaint in complaints %} +
+

{{ complaint.message }}

+ Submitted on: {{ complaint.submitted_at.strftime('%Y-%m-%d %H:%M') }} +
+ {% endfor %} +
+ {% else %} +
+

No complaints have been submitted yet.

+
+ {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/dashboard.html b/submissions/T039_BlueScreeners/templates/dashboard.html new file mode 100644 index 00000000..685319fe --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/dashboard.html @@ -0,0 +1,80 @@ +{% extends "base.html" %} +{% block title %}Dashboard{% endblock %} +{% block content %} + + +
+ {% if session.role in ['admin', 'hod'] %} +

Admin & HOD Dashboard

+ +
+

Student Overview

+
+ + + + + + + + + + + + {% for student in students %} + + + + + + + + + {% else %} + + {% endfor %} + +
Student IDNameAttendance (%)Avg. Score (%)Risk Level
{{ student.id }}{{ student.name }}{{ student.attendance }}{{ student.avg_score }} + + {{ student.risk_level }} + +
No student data available.
+
+
+ + {% elif session.role == 'chatbot' %} +

AI Chatbot Dashboard

+

Manage educational content and bot interactions.

+
+
+

Quiz Management

+

Generate and manage quizzes for students.

+ Generate Quizzes +
+
+

Chatbot Interactions

+

Review and test the student support chatbot.

+ Test Chatbot +
+
+ + {% else %} +

Welcome, {{ session.username }}!

+

This is your dashboard.

+ {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/home.html b/submissions/T039_BlueScreeners/templates/home.html new file mode 100644 index 00000000..69b98a0e --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/home.html @@ -0,0 +1,53 @@ +{% extends "base.html" %} +{% block title %}Welcome{% endblock %} +{% block content %} + + +
+
+

+ + Ensuring No Student is Left Behind + +

+

+ Our AI-powered platform unifies student data to predict at-risk individuals, enabling early and effective intervention. +

+
+
+ +
+
+
+
+

Unify Data

+

Consolidate scattered records like attendance, grades, and activities into a single, clear view.

+
+
+

Predict Risk

+

Our AI model analyzes data to identify students at risk of falling behind, weeks in advance.

+
+
+

Intervene Early

+

Automatically alert mentors and counselors with the insights they need to provide timely, effective support.

+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/leaderboard.html b/submissions/T039_BlueScreeners/templates/leaderboard.html new file mode 100644 index 00000000..c084249b --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/leaderboard.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% block title %}Leaderboard{% endblock %} + +{% block content %} + + +
+

Weekly Quiz Leaderboard

+ +
+ + + + + + + + + + + {% for user in top_users %} + + + + + + + {% endfor %} + +
RankUsernameScoreReward
{{ loop.index }}{{ user.username }}{{ user.score }}{{ user.reward }}
+
+ + +
+{% endblock %} diff --git a/submissions/T039_BlueScreeners/templates/login.html b/submissions/T039_BlueScreeners/templates/login.html new file mode 100644 index 00000000..0a03d9b4 --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/login.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% block title %}Login{% endblock %} +{% block content %} +
+
+

Welcome

+
+
+ + +
+
+ + +
+ +
+
+
+{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/now login page working but i cant login as it giv... - Google Docs.pdf b/submissions/T039_BlueScreeners/templates/now login page working but i cant login as it giv... - Google Docs.pdf new file mode 100644 index 00000000..048138f1 Binary files /dev/null and b/submissions/T039_BlueScreeners/templates/now login page working but i cant login as it giv... - Google Docs.pdf differ diff --git a/submissions/T039_BlueScreeners/templates/points.html b/submissions/T039_BlueScreeners/templates/points.html new file mode 100644 index 00000000..f0cf3a6f --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/points.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% block title %}Weekly Quiz Points{% endblock %} +{% block content %} +
+

Weekly Quiz Points

+
+

Team Rankings

+
    + {% for team, point in points.items() %} +
  • {{ team }}: {{ point }} points
  • + {% endfor %} +
+
+
+{% endblock %} diff --git a/submissions/T039_BlueScreeners/templates/quiz.html b/submissions/T039_BlueScreeners/templates/quiz.html new file mode 100644 index 00000000..16e189b7 --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/quiz.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% block title %}Quiz{% endblock %} +{% block content %} +
+

Quiz

+ {% if quiz %} + {% for item in quiz %} +
+

{{ item.question }}

+
+ {% for option in item.options %} + + {% endfor %} + +
+
+ {% endfor %} + {% else %} +

No quiz available.

+ {% endif %} +
+{% endblock %} diff --git a/submissions/T039_BlueScreeners/templates/register.html b/submissions/T039_BlueScreeners/templates/register.html new file mode 100644 index 00000000..c972f4b5 --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/register.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} +{% block title %}Register{% endblock %} +{% block content %} +
+
+

Create Your Account

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+

+ Already have an account? Login here +

+
+
+{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/stu.png b/submissions/T039_BlueScreeners/templates/stu.png new file mode 100644 index 00000000..7e4db336 Binary files /dev/null and b/submissions/T039_BlueScreeners/templates/stu.png differ diff --git a/submissions/T039_BlueScreeners/templates/student_dashboard.html b/submissions/T039_BlueScreeners/templates/student_dashboard.html new file mode 100644 index 00000000..78fd9b02 --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/student_dashboard.html @@ -0,0 +1,60 @@ +{% extends "base.html" %} +{% block title %}Student Dashboard{% endblock %} + +{% block content %} + + +
+

Your Overview

+ +
+

Attendance

+

If your teacher has started an attendance session, click the button below to mark your presence.

+ Mark My Attendance +
+ +
+
+

Your Attendance

+

92%

+
+

Average Score

+

88%

+
+

Reward Points

+

1250

+
+ +
+
+

Your Assignments

+
    +
  • +
    +

    Calculus Homework 1

    + Due: 2025-10-10 +
    + Submit +
  • +
+
+ + +
+
+{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/submit_complaint.html b/submissions/T039_BlueScreeners/templates/submit_complaint.html new file mode 100644 index 00000000..6d99a9fa --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/submit_complaint.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% block title %}Submit Complaint{% endblock %} + +{% block content %} +
+

Submit Anonymous Complaint

+
+ + +
+
+{% endblock %} diff --git a/submissions/T039_BlueScreeners/templates/teacher_dashboard.html b/submissions/T039_BlueScreeners/templates/teacher_dashboard.html new file mode 100644 index 00000000..e90761ac --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/teacher_dashboard.html @@ -0,0 +1,72 @@ +{% extends "base.html" %} +{% block title %}Teacher Dashboard{% endblock %} + +{% block content %} + + +
+

Your Dashboard

+ +
+

Attendance Session

+ {% if active_session %} +

An attendance session is currently ACTIVE.

+
+ +
+ {% else %} +

No session is active. Click below to start a new 3-minute session for your students.

+
+ +
+ {% endif %} +
+ +
+

Create New Assignment

+
+
+ + +
+ +
+
+ +
+

Your Assignments

+
+ + + + + + + + + + {% for assignment in assignments %} + + + + + + {% else %} + + {% endfor %} + +
TitleSubmissionsActions
{{ assignment.title }}{{ assignment.submissions.count() }} + View & Grade +
You have not created any assignments yet.
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/view_submission.html b/submissions/T039_BlueScreeners/templates/view_submission.html new file mode 100644 index 00000000..1aea61b0 --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/view_submission.html @@ -0,0 +1,46 @@ +{% extends "base.html" %} +{% block title %}View Submissions{% endblock %} + +{% block content %} + +
+

Submissions for: {{ assignment.title }}

+

Here you can view and grade student submissions.

+ +
+
+ + + + + + + + + + + {% for sub in submissions %} + + + + + + + {% else %} + + {% endfor %} + +
Student NameSubmitted AtCurrent MarksEnter Grade
{{ sub.student.full_name }}{{ sub.submitted_at.strftime('%Y-%m-%d %H:%M') }}{{ sub.marks }} / 100 +
+ + +
+
No submissions for this assignment yet.
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/templates/weekly_quiz_status.html b/submissions/T039_BlueScreeners/templates/weekly_quiz_status.html new file mode 100644 index 00000000..c9caa22e --- /dev/null +++ b/submissions/T039_BlueScreeners/templates/weekly_quiz_status.html @@ -0,0 +1,22 @@ + + + + + Weekly Quiz Status + + +

Weekly Quiz Activation

+ {% if status and status.is_active %} +

The weekly quiz is currently Active.

+
+ +
+ {% else %} +

The weekly quiz is currently Inactive.

+
+ +
+ {% endif %} + Back to Dashboard + + diff --git a/submissions/T039_BlueScreeners/test_face_detection.py b/submissions/T039_BlueScreeners/test_face_detection.py new file mode 100644 index 00000000..3ec36817 --- /dev/null +++ b/submissions/T039_BlueScreeners/test_face_detection.py @@ -0,0 +1,25 @@ +# encode_faces.py (Test Version) +import cv2 +import face_recognition +import pickle +import os +import numpy as np + +folderPath = 'student_images' + +# --- TEMPORARY CHANGE --- +# Comment out the original line that reads all files +# pathList = os.listdir(folderPath) +# Add this line to only test the new image +pathList = ['test_face.jpg'] +# --- END TEMPORARY CHANGE --- + +print(f"Found files: {pathList}") + +# (The rest of the file remains the same as the last version I provided) +# ... +imgList = [] +studentUsernames = [] + +for path in pathList: + # ... (rest of the code) \ No newline at end of file diff --git a/submissions/T039_BlueScreeners/test_face_encoding.py b/submissions/T039_BlueScreeners/test_face_encoding.py new file mode 100644 index 00000000..0d02b7c7 --- /dev/null +++ b/submissions/T039_BlueScreeners/test_face_encoding.py @@ -0,0 +1,35 @@ +import cv2 +import numpy as np +import face_recognition +from PIL import Image + +def test_encoding(image_path): + # Load image with cv2 + img = cv2.imread(image_path) + if img is None: + print(f"Failed to load image: {image_path}") + return + # Convert BGR to RGB + img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + # Ensure contiguous array + img_rgb = np.ascontiguousarray(img_rgb) + print(f"Image shape: {img_rgb.shape}, dtype: {img_rgb.dtype}, contiguous: {img_rgb.flags.c_contiguous}") + # Try face encoding + try: + encodings = face_recognition.face_encodings(img_rgb) + print(f"Found {len(encodings)} face encodings") + except Exception as e: + print(f"Error during face encoding: {e}") + + # Alternative: convert to PIL and back to numpy + pil_img = Image.fromarray(img_rgb) + img_rgb_pil = np.array(pil_img) + print(f"PIL converted image shape: {img_rgb_pil.shape}, dtype: {img_rgb_pil.dtype}, contiguous: {img_rgb_pil.flags.c_contiguous}") + try: + encodings_pil = face_recognition.face_encodings(img_rgb_pil) + print(f"Found {len(encodings_pil)} face encodings after PIL conversion") + except Exception as e: + print(f"Error during face encoding after PIL conversion: {e}") + +if __name__ == "__main__": + test_encoding("student_images/Jaskaran_high_risk.jpg") diff --git a/submissions/T039_BlueScreeners/trained_model.pkl b/submissions/T039_BlueScreeners/trained_model.pkl new file mode 100644 index 00000000..b41634a9 Binary files /dev/null and b/submissions/T039_BlueScreeners/trained_model.pkl differ