| from flask import Flask, render_template, request, jsonify, session, redirect, url_for, send_file |
| from flask_sqlalchemy import SQLAlchemy |
| from flask_mail import Mail, Message |
| from werkzeug.security import generate_password_hash, check_password_hash |
| import sqlite3 |
| import pandas as pd |
| import os |
| import cv2 |
| import numpy as np |
| from datetime import datetime, time, date |
| import base64 |
| import json |
| import random |
|
|
| app = Flask(__name__) |
| app.config['SECRET_KEY'] = 'your_secret_key_here' |
| app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db' |
| app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False |
|
|
| |
| app.config['MAIL_SERVER'] = 'smtp.gmail.com' |
| app.config['MAIL_PORT'] = 465 |
| app.config['MAIL_USERNAME'] = 'your_email@gmail.com' |
| app.config['MAIL_PASSWORD'] = 'your_password' |
| app.config['MAIL_USE_TLS'] = False |
| app.config['MAIL_USE_SSL'] = True |
|
|
| db = SQLAlchemy(app) |
| mail = Mail(app) |
|
|
| |
| class User(db.Model): |
| id = db.Column(db.Integer, primary_key=True) |
| full_name = db.Column(db.String(100), nullable=False) |
| email = db.Column(db.String(100), unique=True, nullable=False) |
| username = db.Column(db.String(50), unique=True, nullable=False) |
| password = db.Column(db.String(100), nullable=False) |
| role = db.Column(db.String(10), nullable=False) |
| enrollment_number = db.Column(db.String(20), unique=True, nullable=True) |
| phone_number = db.Column(db.String(20), nullable=True) |
| student_number = db.Column(db.String(20), unique=True, nullable=True) |
| parent_number = db.Column(db.String(20), nullable=True) |
| parent_email = db.Column(db.String(100), nullable=True) |
| selected_class = db.Column(db.String(10), nullable=True) |
| selected_regular_subjects = db.Column(db.Text, nullable=True) |
| selected_specializations = db.Column(db.Text, nullable=True) |
| selected_batch = db.Column(db.String(20), nullable=True) |
| selected_practicals = db.Column(db.Text, nullable=True) |
|
|
| |
| class DailyPin(db.Model): |
| id = db.Column(db.Integer, primary_key=True) |
| class_name = db.Column(db.String(10), nullable=False) |
| pin = db.Column(db.String(4), nullable=False) |
| date = db.Column(db.Date, nullable=False) |
|
|
| |
| def get_db_connection(): |
| conn = sqlite3.connect('config.db') |
| conn.row_factory = sqlite3.Row |
| return conn |
|
|
| def init_timetable_db(): |
| conn = get_db_connection() |
| conn.execute(''' |
| CREATE TABLE IF NOT EXISTS teacher_config ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| class_name TEXT NOT NULL, |
| config_type TEXT NOT NULL, |
| name TEXT NOT NULL |
| ) |
| ''') |
| conn.commit() |
| conn.close() |
|
|
| |
| with app.app_context(): |
| db.create_all() |
| init_timetable_db() |
|
|
| |
| face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml") |
| dataset_path = "./face_dataset/" |
| attendance_path = "./attendance_data/" |
| os.makedirs(dataset_path, exist_ok=True) |
| os.makedirs(attendance_path, exist_ok=True) |
|
|
| |
| def get_daily_pin(class_name): |
| today = date.today() |
| pin_entry = DailyPin.query.filter_by(class_name=class_name, date=today).first() |
| if not pin_entry: |
| pin = f"{random.randint(0, 9999):04d}" |
| pin_entry = DailyPin(class_name=class_name, pin=pin, date=today) |
| db.session.add(pin_entry) |
| db.session.commit() |
| return pin_entry.pin |
|
|
| |
| @app.route('/') |
| def index(): |
| return render_template('index.html') |
|
|
| @app.route('/signup', methods=['POST']) |
| def signup(): |
| data = request.json |
| full_name = data.get('full_name') |
| email = data.get('email') |
| username = data.get('username') |
| password = data.get('password') |
| role = data.get('role') |
| enrollment = data.get('enrollment_number') if role == 'student' else None |
| phone_number = data.get('phone_number') if role == 'teacher' else None |
| student_number = data.get('student_number') if role == 'student' else None |
| parent_number = data.get('parent_number') if role == 'student' else None |
| parent_email = data.get('parent_email') if role == 'student' else None |
|
|
| if not all([full_name, email, username, password, role]): |
| return jsonify({'success': False, 'message': 'All fields are required!'}) |
| if role == 'student' and not enrollment: |
| return jsonify({'success': False, 'message': 'Enrollment number is required for students!'}) |
| if role == 'teacher' and not phone_number: |
| return jsonify({'success': False, 'message': 'Phone number is required for teachers!'}) |
|
|
| if User.query.filter((User.username == username) | (User.email == email) | |
| (User.enrollment_number == enrollment) | (User.student_number == student_number)).first(): |
| return jsonify({'success': False, 'message': 'Username, email, enrollment, or student number already exists!'}) |
|
|
| hashed = generate_password_hash(password) |
| u = User( |
| full_name=full_name, email=email, |
| username=username, password=hashed, role=role, |
| enrollment_number=enrollment, |
| phone_number=phone_number, |
| student_number=student_number, |
| parent_number=parent_number, |
| parent_email=parent_email |
| ) |
| db.session.add(u) |
| db.session.commit() |
| return jsonify({'success': True, 'message': 'Account created successfully! Please log in.'}) |
|
|
| @app.route('/login', methods=['POST']) |
| def login(): |
| data = request.json |
| username = data.get('username') |
| password = data.get('password') |
| role = data.get('role') |
|
|
| user = User.query.filter_by(username=username, role=role).first() |
| if user and check_password_hash(user.password, password): |
| session['user_id'] = user.id |
| session['role'] = user.role |
| redirect_url = '/teacher_dashboard' if role == 'teacher' else '/student_dashboard' |
| return jsonify({'success': True, 'redirect': redirect_url}) |
| return jsonify({'success': False, 'message': 'Invalid username or password'}) |
|
|
| @app.route('/logout') |
| def logout(): |
| session.clear() |
| return redirect(url_for('index')) |
|
|
| @app.route('/teacher_dashboard') |
| def teacher_dashboard(): |
| if 'user_id' not in session or session['role'] != 'teacher': |
| return redirect(url_for('index')) |
| user = db.session.get(User, session['user_id']) |
| pin = get_daily_pin(user.selected_class) if user.selected_class else None |
| return render_template('teacher_dashboard.html', user=user, selected_class=user.selected_class, daily_pin=pin) |
|
|
| @app.route('/api/update_teacher_profile', methods=['POST']) |
| def update_teacher_profile(): |
| if 'user_id' not in session or session['role'] != 'teacher': |
| return jsonify({'success': False, 'message': 'Unauthorized'}), 401 |
| data = request.json |
| user = db.session.get(User, session['user_id']) |
| user.full_name = data['full_name'] |
| user.email = data['email'] |
| user.phone_number = data['phone_number'] |
| db.session.commit() |
| return jsonify({'success': True, 'message': 'Profile updated'}) |
|
|
| @app.route('/get_students', methods=['GET']) |
| def get_students(): |
| class_name = request.args.get('class') |
| students = User.query.filter_by(role='student', selected_class=class_name).all() |
| students_data = [{'id': s.id, 'full_name': s.full_name, 'enrollment_number': s.enrollment_number} for s in students] |
| return jsonify({'students': students_data}) |
|
|
| @app.route('/api/select_teacher_class', methods=['POST']) |
| def select_teacher_class(): |
| if 'user_id' not in session or session['role'] != 'teacher': |
| return jsonify({'success': False}), 401 |
| data = request.json |
| cls = data.get('class') |
| user = db.session.get(User, session['user_id']) |
| user.selected_class = cls |
| db.session.commit() |
| return jsonify({'success': True}) |
|
|
| @app.route('/get_student_details', methods=['GET']) |
| def get_student_details(): |
| student_id = request.args.get('id') |
| student = db.session.get(User, student_id) |
| if not student or student.role != 'student': |
| return jsonify({'error': 'Student not found'}), 404 |
|
|
| subject_attendance = get_subject_attendance(student) |
|
|
| regular_subjects = [{'name': subj, 'attendance_count': subject_attendance['regular'].get(subj, {'attendance': 0})['attendance'], 'total_count': subject_attendance['regular'].get(subj, {'total': 0})['total']} for subj in json.loads(student.selected_regular_subjects or '[]')] |
| specialization_subjects = [{'name': subj, 'attendance_count': subject_attendance['specialization'].get(subj, {'attendance': 0})['attendance'], 'total_count': subject_attendance['specialization'].get(subj, {'total': 0})['total']} for subj in json.loads(student.selected_specializations or '[]')] |
| practical_subjects = [{'name': subj, 'batch': student.selected_batch or 'N/A', 'attendance_count': subject_attendance['practical'].get(subj, {'attendance': 0})['attendance'], 'total_count': subject_attendance['practical'].get(subj, {'total': 0})['total']} for subj in json.loads(student.selected_practicals or '[]')] |
|
|
| face_folder = os.path.join(dataset_path, student.selected_class or '') |
| face_file = f"{student.enrollment_number}_{student.full_name}.npy" |
| face_registered = os.path.exists(os.path.join(face_folder, face_file)) |
|
|
| student_data = { |
| 'enrollment_number': student.enrollment_number, |
| 'email': student.email, |
| 'student_number': student.student_number, |
| 'parent_number': student.parent_number, |
| 'parent_email': student.parent_email, |
| 'regular_subjects': regular_subjects, |
| 'specialization_subjects': specialization_subjects, |
| 'practical_subjects': practical_subjects, |
| 'face_registered': face_registered |
| } |
| return jsonify(student_data) |
|
|
| @app.route('/student_dashboard') |
| def student_dashboard(): |
| if 'user_id' not in session or session['role'] != 'student': |
| return redirect(url_for('index')) |
| user = db.session.get(User, session['user_id']) |
| face_folder = os.path.join(dataset_path, (user.selected_class or '')) |
| face_file = f"{user.enrollment_number}_{user.full_name}.npy" |
| face_exists = os.path.exists(os.path.join(face_folder, face_file)) |
| subject_attendance = get_subject_attendance(user) |
| teachers = User.query.filter_by(role='teacher', selected_class=user.selected_class).all() |
| return render_template('student_dashboard.html', |
| user=user, |
| selected_regular_subjects=json.loads(user.selected_regular_subjects or '[]'), |
| selected_specializations=json.loads(user.selected_specializations or '[]'), |
| selected_practicals=json.loads(user.selected_practicals or '[]'), |
| subject_attendance=subject_attendance, |
| face_exists=face_exists, |
| teachers=teachers) |
|
|
| @app.route('/api/update_profile', methods=['POST']) |
| def update_profile(): |
| if 'user_id' not in session or session['role'] != 'student': |
| return jsonify({'success': False, 'message': 'Unauthorized'}), 401 |
| data = request.json |
| user = db.session.get(User, session['user_id']) |
| user.full_name = data['full_name'] |
| user.enrollment_number = data['enrollment_number'] |
| user.email = data['email'] |
| user.student_number = data['student_number'] |
| user.parent_number = data['parent_number'] |
| user.parent_email = data['parent_email'] |
| db.session.commit() |
| return jsonify({'success': True, 'message': 'Profile updated'}) |
|
|
| @app.route('/api/select_class', methods=['POST']) |
| def api_select_class(): |
| if 'user_id' not in session or session['role'] != 'student': |
| return jsonify({'success': False}), 401 |
| data = request.json |
| cls = data.get('class') |
| user = db.session.get(User, session['user_id']) |
| user.selected_class = cls |
| db.session.commit() |
| return jsonify({'success': True}) |
|
|
| @app.route('/api/update_regular_subjects', methods=['POST']) |
| def update_regular_subjects(): |
| if 'user_id' not in session: |
| return jsonify(success=False), 401 |
| subjects = request.json.get('subjects') |
| u = db.session.get(User, session['user_id']) |
| u.selected_regular_subjects = json.dumps(subjects) |
| db.session.commit() |
| return jsonify(success=True) |
|
|
| @app.route('/api/update_specializations', methods=['POST']) |
| def update_specializations(): |
| if 'user_id' not in session: |
| return jsonify(success=False), 401 |
| subjects = request.json.get('subjects') |
| u = db.session.get(User, session['user_id']) |
| u.selected_specializations = json.dumps(subjects) |
| db.session.commit() |
| return jsonify(success=True) |
|
|
| @app.route('/api/update_batch', methods=['POST']) |
| def update_batch(): |
| if 'user_id' not in session: |
| return jsonify(success=False), 401 |
| batch = request.json.get('batch') |
| u = db.session.get(User, session['user_id']) |
| u.selected_batch = batch |
| db.session.commit() |
| return jsonify(success=True) |
|
|
| @app.route('/api/update_practicals', methods=['POST']) |
| def update_practicals(): |
| if 'user_id' not in session: |
| return jsonify(success=False), 401 |
| practs = request.json.get('practicals') |
| u = db.session.get(User, session['user_id']) |
| u.selected_practicals = json.dumps(practs) |
| db.session.commit() |
| return jsonify(success=True) |
|
|
| def get_excel_filename(class_name): |
| if class_name: |
| return f"{class_name}_timetable.xlsx" |
| return "timetable.xlsx" |
|
|
| @app.route('/timetable') |
| def timetable_page(): |
| if 'user_id' not in session or session['role'] != 'teacher': |
| return redirect(url_for('index')) |
| return render_template('timetable.html') |
|
|
| @app.route('/add_config', methods=['POST']) |
| def add_config(): |
| data = request.get_json() |
| class_name = data.get("class") |
| config_type = data.get("type") |
| name = data.get("name") |
| if not (class_name and config_type and name): |
| return jsonify({"error": "Missing parameters"}), 400 |
| conn = get_db_connection() |
| conn.execute('INSERT INTO teacher_config (class_name, config_type, name) VALUES (?, ?, ?)', |
| (class_name, config_type, name)) |
| conn.commit() |
| conn.close() |
| return jsonify({"message": "Added successfully"}) |
|
|
| @app.route('/delete_config', methods=['POST']) |
| def delete_config(): |
| data = request.get_json() |
| class_name = data.get("class") |
| config_type = data.get("type") |
| name = data.get("name") |
| if not (class_name and config_type and name): |
| return jsonify({"error": "Missing parameters"}), 400 |
| conn = get_db_connection() |
| conn.execute('DELETE FROM teacher_config WHERE class_name=? AND config_type=? AND name=?', |
| (class_name, config_type, name)) |
| conn.commit() |
| conn.close() |
| return jsonify({"message": "Deleted successfully"}) |
|
|
| @app.route('/get_config', methods=['GET']) |
| def get_config(): |
| class_name = request.args.get("class") |
| config_type = request.args.get("type") |
| if not (class_name and config_type): |
| return jsonify([]) |
| conn = get_db_connection() |
| configs = conn.execute('SELECT name FROM teacher_config WHERE class_name=? AND config_type=?', |
| (class_name, config_type)).fetchall() |
| conn.close() |
| return jsonify([row["name"] for row in configs]) |
|
|
| @app.route('/load', methods=['GET']) |
| def load_timetable(): |
| class_name = request.args.get('class') |
| filename = get_excel_filename(class_name) |
| if not os.path.exists(filename): |
| return jsonify({}) |
| df = pd.read_excel(filename, index_col=0) |
| return df.to_json() |
|
|
| @app.route('/save', methods=['POST']) |
| def save_timetable(): |
| payload = request.get_json() |
| teacher_class = payload.get("class", "") |
| data = payload.get("timetable", {}) |
| filename = get_excel_filename(teacher_class) |
| df = pd.DataFrame(data) |
| df.to_excel(filename) |
| return jsonify({'message': f'Saved successfully to {filename}!'}) |
|
|
| @app.route('/download_excel', methods=['GET']) |
| def download_excel(): |
| class_name = request.args.get('class') |
| filename = get_excel_filename(class_name) |
| if not os.path.exists(filename): |
| return "File not found", 404 |
| return send_file(filename, as_attachment=True) |
|
|
| def distance(v1, v2): |
| return np.sqrt(((v1 - v2) ** 2).sum()) |
|
|
| def knn(train, test, k=5): |
| dist = [] |
| for i in range(train.shape[0]): |
| ix = train[i, :-1] |
| iy = train[i, -1] |
| d = distance(test, ix) |
| dist.append([d, iy]) |
| dk = sorted(dist, key=lambda x: x[0])[:k] |
| labels = np.array(dk)[:, -1] |
| return np.unique(labels, return_counts=True)[0][0] |
|
|
| class AttendanceSystem: |
| def __init__(self, class_name): |
| self.class_name = class_name |
| self.main_file = os.path.join(attendance_path, f"{class_name}.xlsx") |
| self.pending_file = os.path.join(attendance_path, f"{class_name}_pending.xlsx") |
| self.columns = ["Enrollment", "Name", "Date", "Day", "CheckIn", "CheckOut"] |
| |
| for f in [self.main_file, self.pending_file]: |
| if not os.path.exists(f): |
| pd.DataFrame(columns=self.columns).to_excel(f, index=False) |
|
|
| def checkin(self, enrollment, name, pin): |
| today = datetime.now().strftime("%Y-%m-%d") |
| correct_pin = get_daily_pin(self.class_name) |
| if pin != correct_pin: |
| return False, "Invalid PIN." |
|
|
| now = datetime.now() |
| checkin_time = now.strftime("%H.%M") |
| |
| df_main = pd.read_excel(self.main_file) |
| if ((df_main["Enrollment"] == enrollment) & (df_main["Date"] == today)).any(): |
| return False, "You have already checked in and checked out today." |
|
|
| df_pending = pd.read_excel(self.pending_file) |
| if ((df_pending["Enrollment"] == enrollment) & (df_pending["Date"] == today)).any(): |
| return False, "You have already checked in today. Please check out first." |
|
|
| if now.time() < datetime.strptime("08:00", "%H:%M").time(): |
| return False, "Check-in starts at 8 AM." |
|
|
| day_name = now.strftime("%A") |
| new_entry = pd.DataFrame([[enrollment, name, today, day_name, checkin_time, ""]], |
| columns=self.columns) |
| df_pending = pd.concat([df_pending, new_entry], ignore_index=True) |
| df_pending.to_excel(self.pending_file, index=False) |
| |
| return True, "Check-in successful. Please remember to check out later." |
|
|
| def checkout(self, enrollment): |
| now = datetime.now() |
| today = now.strftime("%Y-%m-%d") |
| checkout_time = now.strftime("%H.%M") |
|
|
| df_pending = pd.read_excel(self.pending_file) |
| df_main = pd.read_excel(self.main_file) |
|
|
| has_main = ((df_main["Enrollment"] == enrollment) & (df_main["Date"] == today)).any() |
| has_pending = ((df_pending["Enrollment"] == enrollment) & (df_pending["Date"] == today)).any() |
|
|
| if not has_pending and has_main: |
| return False, "You have already checked out today." |
|
|
| if not has_pending: |
| return False, "No pending check-in found. Please check in first." |
|
|
| if now.time() > datetime.strptime("18:00", "%H:%M").time(): |
| return False, "You missed check-out time. Contact your class teacher." |
|
|
| idx = df_pending[(df_pending["Enrollment"] == enrollment) & (df_pending["Date"] == today)].index[0] |
| pending_entry = df_pending.loc[idx] |
|
|
| new_row = { |
| "Enrollment": pending_entry["Enrollment"], |
| "Name": pending_entry["Name"], |
| "Date": today, |
| "Day": pending_entry["Day"], |
| "CheckIn": pending_entry["CheckIn"], |
| "CheckOut": checkout_time |
| } |
| df_main = pd.concat([df_main, pd.DataFrame([new_row])], ignore_index=True) |
| df_main.to_excel(self.main_file, index=False) |
|
|
| df_pending = df_pending.drop(idx) |
| df_pending.to_excel(self.pending_file, index=False) |
| |
| return True, "Check-out successful. Attendance recorded." |
|
|
| import pandas as pd |
| from datetime import datetime, time |
|
|
| def parse_time(val): |
| if pd.isna(val): |
| return None |
| s = str(val).replace('.', ':') |
| if ':' not in s and len(s) >= 3 and s.isdigit(): |
| s = s[:-2] + ':' + s[-2:] |
| try: |
| return datetime.strptime(s, '%H:%M').time() |
| except ValueError: |
| return datetime.strptime(s, '%I:%M').time() |
|
|
| def parse_timetable_time(time_str): |
| time_str = time_str.replace('.', ':') |
| hour, minute = map(int, time_str.split(':')) |
| if 1 <= hour <= 5: |
| hour += 12 |
| return time(hour, minute) |
|
|
| def get_slot_type(content): |
| content = "" if pd.isna(content) else str(content) |
| if "Batch" in content: |
| return "practical" |
| elif "<br>" in content: |
| return "specialization" |
| elif content in ["Short Break", "Lunch Break"]: |
| return "break" |
| elif content.strip() == "": |
| return "break" |
| else: |
| return "regular" |
|
|
| def parse_practical(content): |
| lines = content.split('<br>') |
| batch_subjects = {} |
| for line in lines: |
| if ':' in line: |
| batch, subject = line.split(':') |
| batch = batch.strip().replace('Batch ', '') |
| subject = subject.strip() |
| batch_subjects[batch] = subject |
| return batch_subjects |
|
|
| def parse_specialization(content): |
| return [subj.strip() for subj in content.split('<br>')] |
|
|
| def get_slots(df, day): |
| row = df.loc[day] |
| slots = [] |
| for col in df.columns: |
| start_str, end_str = col.split(" - ") |
| start_time = parse_timetable_time(start_str) |
| end_time = parse_timetable_time(end_str) |
| content = row[col] |
| if pd.isna(content) or str(content).strip() == "": |
| continue |
| slots.append({ |
| "start": start_time, |
| "end": end_time, |
| "content": str(content) |
| }) |
| return slots |
|
|
| def get_subject_attendance(user): |
| class_name = user.selected_class |
| attendance_file = os.path.join(attendance_path, f"{class_name}.xlsx") |
| if not os.path.exists(attendance_file): |
| return {'regular': {}, 'specialization': {}, 'practical': {}} |
| df_att = pd.read_excel(attendance_file) |
| all_dates = df_att['Date'].unique() |
| timetable_file = f"{class_name}_timetable.xlsx" |
| if not os.path.exists(timetable_file): |
| return {'regular': {}, 'specialization': {}, 'practical': {}} |
| df_timetable = pd.read_excel(timetable_file, index_col=0) |
| regular_counts = {subj: {'attendance': 0, 'total': 0} for subj in json.loads(user.selected_regular_subjects or '[]')} |
| spec_counts = {subj: {'attendance': 0, 'total': 0} for subj in json.loads(user.selected_specializations or '[]')} |
| prac_counts = {subj: {'attendance': 0, 'total': 0} for subj in json.loads(user.selected_practicals or '[]')} |
| for date in all_dates: |
| day = datetime.strptime(date, '%Y-%m-%d').strftime('%A') |
| if day not in df_timetable.index: |
| continue |
| slots = get_slots(df_timetable, day) |
| student_att = df_att[(df_att['Enrollment'] == user.enrollment_number) & (df_att['Date'] == date)] |
| if not student_att.empty: |
| check_in_str = student_att['CheckIn'].values[0] |
| check_out_str = student_att['CheckOut'].values[0] |
| check_in_time = parse_time(check_in_str) |
| check_out_time = parse_time(check_out_str) |
| else: |
| check_in_time = None |
| check_out_time = None |
| for slot in slots: |
| slot_start = slot['start'] |
| slot_end = slot['end'] |
| content = slot['content'] |
| slot_type = get_slot_type(content) |
| if slot_type == 'regular': |
| subject = content |
| if subject in regular_counts: |
| regular_counts[subject]['total'] += 1 |
| if check_in_time and check_in_time <= slot_start and check_out_time >= slot_end: |
| regular_counts[subject]['attendance'] += 1 |
| elif slot_type == 'specialization': |
| subjects = parse_specialization(content) |
| selected_specs = json.loads(user.selected_specializations or '[]') |
| for selected_spec in selected_specs: |
| if selected_spec in subjects: |
| spec_counts[selected_spec]['total'] += 1 |
| if check_in_time and check_in_time <= slot_start and check_out_time >= slot_end: |
| spec_counts[selected_spec]['attendance'] += 1 |
| elif slot_type == 'practical': |
| batch_subjects = parse_practical(content) |
| if user.selected_batch in batch_subjects: |
| subject = batch_subjects[user.selected_batch] |
| if subject in prac_counts: |
| prac_counts[subject]['total'] += 1 |
| if check_in_time and check_in_time <= slot_start and check_out_time >= slot_end: |
| prac_counts[subject]['attendance'] += 1 |
| return { |
| 'regular': regular_counts, |
| 'specialization': spec_counts, |
| 'practical': prac_counts |
| } |
|
|
| @app.route('/check_attendance', methods=['GET']) |
| def check_attendance(): |
| students = User.query.filter_by(role='student').all() |
| for student in students: |
| subject_attendance = get_subject_attendance(student) |
| total_attendance = 0 |
| total_possible = 0 |
| for subj_type in ['regular', 'specialization', 'practical']: |
| for subj, counts in subject_attendance[subj_type].items(): |
| total_attendance += counts['attendance'] |
| total_possible += counts['total'] |
| if total_possible > 0: |
| overall_percentage = (total_attendance / total_possible) * 100 |
| if overall_percentage < 75: |
| details = { |
| 'regular': [], |
| 'specialization': [], |
| 'practical': [] |
| } |
| for subj_type in ['regular', 'specialization', 'practical']: |
| for subj, counts in subject_attendance[subj_type].items(): |
| percentage = (counts['attendance'] / counts['total'] * 100) if counts['total'] > 0 else 0 |
| details[subj_type].append({ |
| 'subject': subj, |
| 'attendance_count': counts['attendance'], |
| 'total_count': counts['total'], |
| 'percentage': round(percentage, 2) |
| }) |
| send_attendance_email(student, overall_percentage, details) |
| return 'Attendance check completed' |
|
|
| def send_attendance_email(student, overall_percentage, details): |
| subject = "Low Attendance Alert" |
| recipients = [student.email] |
| if student.parent_email: |
| recipients.append(student.parent_email) |
| body = f""" |
| Dear {student.full_name}, |
| |
| Your overall attendance average is {overall_percentage:.2f}%, which is below the required 75%. |
| |
| Here are your attendance details: |
| |
| Regular Subjects: |
| """ |
| for subj in details['regular']: |
| body += f"- {subj['subject']}: {subj['attendance_count']}/{subj['total_count']} ({subj['percentage']}%\n" |
| body += "\nSpecialization Subjects:\n" |
| for subj in details['specialization']: |
| body += f"- {subj['subject']}: {subj['attendance_count']}/{subj['total_count']} ({subj['percentage']}%\n" |
| body += "\nPractical Subjects:\n" |
| for subj in details['practical']: |
| body += f"- {subj['subject']}: {subj['attendance_count']}/{subj['total_count']} ({subj['percentage']}%\n" |
| body += "\nPlease take necessary actions to improve your attendance.\n\nBest regards,\nAttendance System" |
|
|
| msg = Message(subject, recipients=recipients, body=body) |
| mail.send(msg) |
|
|
| @app.route('/mark_attendance') |
| def mark_attendance_page(): |
| return render_template('mark.html') |
|
|
| @app.route('/register_face') |
| def register_face_page(): |
| if 'user_id' not in session or session['role'] != 'student': |
| return redirect(url_for('index')) |
| return render_template('register.html') |
|
|
| @app.route('/api/register_face', methods=['POST']) |
| def register_face(): |
| data = request.json |
| class_name = data['class_name'] |
| name = data['name'] |
| enrollment = data['enrollment'] |
| images = data['images'] |
|
|
| class_folder = os.path.join(dataset_path, class_name) |
| os.makedirs(class_folder, exist_ok=True) |
|
|
| face_data = [] |
| for img_data in images: |
| img_bytes = base64.b64decode(img_data.split(",")[1]) |
| np_arr = np.frombuffer(img_bytes, np.uint8) |
| img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR) |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
| gray = cv2.equalizeHist(gray) |
| faces = face_cascade.detectMultiScale(gray, 1.3, 5) |
| for (x, y, w, h) in faces[:1]: |
| face = img[y:y+h, x:x+w] |
| face = cv2.resize(face, (100, 100)) |
| face_data.append(face.flatten()) |
| flipped = cv2.flip(face, 1) |
| face_data.append(flipped.flatten()) |
|
|
| if face_data: |
| face_data = np.array(face_data) |
| filename = f"{enrollment}_{name}.npy" |
| np.save(os.path.join(class_folder, filename), face_data) |
| return jsonify({"status": "success", "message": f"{len(face_data)} faces saved"}) |
| return jsonify({"status": "fail", "message": "No faces detected"}) |
|
|
| @app.route('/api/identify_face', methods=['POST']) |
| def identify_face(): |
| data = request.json |
| class_name = data['class_name'] |
| img_data = data['image'] |
| img_bytes = base64.b64decode(img_data.split(",")[1]) |
| np_arr = np.frombuffer(img_bytes, np.uint8) |
| img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR) |
|
|
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
| faces = face_cascade.detectMultiScale(gray, 1.3, 5) |
|
|
| class_folder = os.path.join(dataset_path, class_name) |
| if not os.path.exists(class_folder): |
| return jsonify({"status": "fail", "message": "No data for this class"}) |
|
|
| face_data = [] |
| labels = [] |
| names = {} |
| class_id = 0 |
| for file in os.listdir(class_folder): |
| if file.endswith('.npy'): |
| data_arr = np.load(os.path.join(class_folder, file)) |
| face_data.append(data_arr) |
| parts = file[:-4].split('_', 1) |
| labels.extend([class_id] * data_arr.shape[0]) |
| names[class_id] = {'enrollment': parts[0], 'name': parts[1]} |
| class_id += 1 |
|
|
| if not face_data: |
| return jsonify({"status": "fail", "message": "No trained data found"}) |
|
|
| X_train = np.concatenate(face_data, axis=0) |
| y_train = np.array(labels).reshape(-1, 1) |
| trainset = np.hstack((X_train, y_train)) |
|
|
| for (x, y, w, h) in faces[:1]: |
| face = img[y:y+h, x:x+w] |
| face = cv2.resize(face, (100, 100)).flatten() |
| pred_id = knn(trainset, face) |
| info = names.get(pred_id) |
| if info: |
| return jsonify({ |
| "status": "success", |
| "name": info['name'], |
| "enrollment": info['enrollment'] |
| }) |
| return jsonify({"status": "fail", "message": "Face not recognized"}) |
|
|
| @app.route('/api/checkin', methods=['POST']) |
| def api_checkin(): |
| data = request.json |
| class_name = data['class_name'] |
| enrollment = data['enrollment'] |
| name = data['name'] |
| pin = data.get('pin') |
| attendance = AttendanceSystem(class_name) |
| ok, msg = attendance.checkin(enrollment, name, pin) |
| status = "success" if ok else "fail" |
| return jsonify({"status": status, "message": msg}) |
|
|
| @app.route('/api/checkout', methods=['POST']) |
| def api_checkout(): |
| data = request.json |
| class_name = data['class_name'] |
| enrollment = data['enrollment'] |
| attendance = AttendanceSystem(class_name) |
| ok, msg = attendance.checkout(enrollment) |
| status = "success" if ok else "fail" |
| return jsonify({"status": status, "message": msg}) |
|
|
| |
| @app.route('/api/get_attendance_average', methods=['POST']) |
| def get_attendance_average(): |
| data = request.json |
| class_name = data['class_name'] |
| enrollment = data['enrollment'] |
| user = User.query.filter_by(enrollment_number=enrollment, selected_class=class_name, role='student').first() |
| if not user: |
| return jsonify({'error': 'Student not found'}), 404 |
| subject_attendance = get_subject_attendance(user) |
| total_attendance = 0 |
| total_possible = 0 |
| for subj_type in ['regular', 'specialization', 'practical']: |
| for subj, counts in subject_attendance[subj_type].items(): |
| total_attendance += counts['attendance'] |
| total_possible += counts['total'] |
| if total_possible > 0: |
| overall_percentage = (total_attendance / total_possible) * 100 |
| else: |
| overall_percentage = 0 |
| return jsonify({'overall_percentage': round(overall_percentage, 2)}) |
|
|
| if __name__ == '__main__': |
| app.run(host='0.0.0.0', port=7860, debug=True) |