Add app_function.py
This commit is contained in:
		
							parent
							
								
									1760f36f7b
								
							
						
					
					
						commit
						32cbad1698
					
				
							
								
								
									
										924
									
								
								app_function.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										924
									
								
								app_function.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,924 @@ | |||||||
|  | from flask import Flask, request, jsonify, redirect, url_for, render_template, session, make_response, flash | ||||||
|  | from flask_mail import Mail, Message | ||||||
|  | from flask_bcrypt import Bcrypt | ||||||
|  | import mysql.connector | ||||||
|  | import boto3 | ||||||
|  | from botocore.client import Config | ||||||
|  | import os | ||||||
|  | import logging | ||||||
|  | import random | ||||||
|  | import pandas as pd | ||||||
|  | from dotenv import load_dotenv | ||||||
|  | from flask import send_file | ||||||
|  | import io | ||||||
|  | from functools import wraps | ||||||
|  | import tempfile | ||||||
|  | import shutil | ||||||
|  | import zipfile | ||||||
|  | import time | ||||||
|  | from urllib.parse import quote | ||||||
|  | 
 | ||||||
|  | load_dotenv() | ||||||
|  | bcrypt = Bcrypt() | ||||||
|  | 
 | ||||||
|  | # 配置 AWS S3 | ||||||
|  | aws_access_key_id = os.getenv('AWS_ACCESS_KEY_ID') | ||||||
|  | aws_secret_access_key = os.getenv('AWS_SECRET_ACCESS_KEY') | ||||||
|  | region_name = os.getenv('AWS_REGION') | ||||||
|  | bucket_name = os.getenv('S3_BUCKET_NAME') | ||||||
|  | s3_client = boto3.client( | ||||||
|  |     's3', | ||||||
|  |     aws_access_key_id=aws_access_key_id, | ||||||
|  |     aws_secret_access_key=aws_secret_access_key, | ||||||
|  |     region_name=region_name, | ||||||
|  |     config=Config(signature_version='s3v4') | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | # 下载相关 | ||||||
|  | def download_and_zip_files(bucket_name, folder_key): | ||||||
|  |     """下载文件并进行支持 ZIP64 的压缩打包""" | ||||||
|  |     temp_dir = tempfile.mkdtemp()  # 创建一个临时目录 | ||||||
|  |     zip_file_path = os.path.join(temp_dir, 'homework.zip') | ||||||
|  | 
 | ||||||
|  |     try: | ||||||
|  |         # 列出文件 | ||||||
|  |         file_keys = list_files_in_folder(bucket_name, folder_key) | ||||||
|  | 
 | ||||||
|  |         if not file_keys: | ||||||
|  |             logging.error(f"No files found in folder: {folder_key}") | ||||||
|  |             return None | ||||||
|  | 
 | ||||||
|  |         # 下载文件 | ||||||
|  |         for file_key in file_keys: | ||||||
|  |             file_name = os.path.basename(file_key) | ||||||
|  |             file_download_path = os.path.join(temp_dir, file_name) | ||||||
|  | 
 | ||||||
|  |             # 下载文件 | ||||||
|  |             logging.info(f"Downloading {file_key} to {file_download_path}") | ||||||
|  |             s3_client.download_file(bucket_name, file_key, file_download_path) | ||||||
|  |          | ||||||
|  |         # ZIP 压缩过程 | ||||||
|  |         with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) as zipf: | ||||||
|  |             for root, _, files in os.walk(temp_dir): | ||||||
|  |                 for file in files: | ||||||
|  |                     file_path = os.path.join(root, file) | ||||||
|  | 
 | ||||||
|  |                     # 排除自身的 ZIP 文件,防止自我压缩 | ||||||
|  |                     if file == os.path.basename(zip_file_path): | ||||||
|  |                         logging.info(f"Skipping self zip file: {file}") | ||||||
|  |                         continue | ||||||
|  |                      | ||||||
|  |                     # 打包其它文件 | ||||||
|  |                     arcname = os.path.relpath(file_path, start=temp_dir) | ||||||
|  |                     logging.info(f"Adding {file} to archive as {arcname}") | ||||||
|  |                     zipf.write(file_path, arcname=arcname) | ||||||
|  | 
 | ||||||
|  |         logging.info(f"Zip file created at {zip_file_path}") | ||||||
|  |         return zip_file_path  # 返回 ZIP 文件路径 | ||||||
|  | 
 | ||||||
|  |     except Exception as e: | ||||||
|  |         logging.error(f"Error during download or zip operation: {e}") | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def upload_zip_to_s3(zip_file_path, bucket_name, object_key): | ||||||
|  |     """上传压缩包文件到S3""" | ||||||
|  |     try: | ||||||
|  |         s3_client.upload_file(zip_file_path, bucket_name, object_key) | ||||||
|  |     except ClientError as e: | ||||||
|  |         logging.error(f"Error uploading zip to S3: {e}") | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  |     return generate_presigned_download_url(bucket_name, object_key) | ||||||
|  | 
 | ||||||
|  | # 列出文件 | ||||||
|  | def list_files_in_folder(bucket_name, folder_key): | ||||||
|  |     try: | ||||||
|  |         response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix=folder_key) | ||||||
|  |         if 'Contents' not in response: | ||||||
|  |             logging.error(f"No contents found in S3 folder {folder_key}") | ||||||
|  |             return [] | ||||||
|  |          | ||||||
|  |         files = [obj['Key'] for obj in response['Contents'] if obj['Key'] != folder_key] | ||||||
|  |         logging.info(f"Files in folder {folder_key}: {files}") | ||||||
|  |         return files | ||||||
|  |      | ||||||
|  |     except ClientError as e: | ||||||
|  |         logging.error(f"Error listing files in folder {folder_key}: {e}") | ||||||
|  |         return [] | ||||||
|  | 
 | ||||||
|  | # 数据库连接函数 | ||||||
|  | def get_db_connection(): | ||||||
|  |     return mysql.connector.connect( | ||||||
|  |         host=os.getenv('MYSQL_HOST'), | ||||||
|  |         user=os.getenv('MYSQL_USER'), | ||||||
|  |         password=os.getenv('MYSQL_PASSWORD'), | ||||||
|  |         database=os.getenv('MYSQL_DB') | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | # 验证学生身份 | ||||||
|  | def validate_student(student_id, password, bcrypt): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  |     cursor.execute('SELECT * FROM students WHERE id = %s', (student_id,)) | ||||||
|  |     student = cursor.fetchone() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  |     if student and bcrypt.check_password_hash(student['password'], password): | ||||||
|  |         return student | ||||||
|  |     return None | ||||||
|  | 
 | ||||||
|  | # 验证管理员身份 | ||||||
|  | def validate_admin(username, password): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  |     cursor.execute('SELECT * FROM administrators WHERE username = %s', (username,)) | ||||||
|  |     admin = cursor.fetchone() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  | 
 | ||||||
|  |     if admin and bcrypt.check_password_hash(admin['password'], password): | ||||||
|  |         return admin | ||||||
|  |     return None | ||||||
|  | 
 | ||||||
|  | # 验证教师身份 | ||||||
|  | def validate_teacher(email, password, bcrypt): | ||||||
|  |     conn = get_db_connection()  # 或者其他方法来获取数据库连接 | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  |     cursor.execute('SELECT * FROM teachers WHERE email = %s', (email,)) | ||||||
|  |     teacher = cursor.fetchone() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  | 
 | ||||||
|  |     if teacher and bcrypt.check_password_hash(teacher['password'], password): | ||||||
|  |         return teacher | ||||||
|  |     return None | ||||||
|  | 
 | ||||||
|  | def fetch_all_departments(): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  |     cursor.execute('SELECT * FROM departments') | ||||||
|  |     departments = cursor.fetchall() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  |     return departments | ||||||
|  | 
 | ||||||
|  | def fetch_all_grades(): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  |     cursor.execute('SELECT * FROM grades') | ||||||
|  |     grades = cursor.fetchall() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  |     return grades | ||||||
|  | 
 | ||||||
|  | def fetch_all_classes(): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  |     cursor.execute('SELECT * FROM classes') | ||||||
|  |     classes = cursor.fetchall() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  |     return classes | ||||||
|  | 
 | ||||||
|  | def fetch_all_teachers(): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  |     cursor.execute('SELECT * FROM teachers') | ||||||
|  |     teachers = cursor.fetchall() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  |     return teachers | ||||||
|  | 
 | ||||||
|  | def fetch_teacher_classes(teacher_id): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  | 
 | ||||||
|  |     # 假设每个教师和班级的关联存储在一个中间表 `class_teacher` 中 | ||||||
|  |     cursor.execute(''' | ||||||
|  |         SELECT c.id, c.name FROM classes c | ||||||
|  |         JOIN class_teacher ct ON c.id = ct.class_id | ||||||
|  |         WHERE ct.teacher_id = %s | ||||||
|  |     ''', (teacher_id,)) | ||||||
|  | 
 | ||||||
|  |     classes = cursor.fetchall() | ||||||
|  | 
 | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  |     return classes | ||||||
|  | 
 | ||||||
|  | def fetch_class_assignments(class_id): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  |     pivot_query = ''' | ||||||
|  |         SELECT * FROM assignments | ||||||
|  |         WHERE class_id = %s | ||||||
|  |     ''' | ||||||
|  |     cursor.execute(pivot_query, (class_id,)) | ||||||
|  |     assignments = cursor.fetchall() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  |     return assignments | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def fetch_class_students(class_id): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor(dictionary=True) | ||||||
|  | 
 | ||||||
|  |     # 查询属于给定class_id的所有学生 | ||||||
|  |     cursor.execute(''' | ||||||
|  |         SELECT * FROM students | ||||||
|  |         WHERE class_id = %s | ||||||
|  |     ''', (class_id,)) | ||||||
|  | 
 | ||||||
|  |     students = cursor.fetchall() | ||||||
|  | 
 | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  |     return students | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # 添加到提交历史表 | ||||||
|  | def add_to_submission_history(student_id, assignment_id, filename): | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor() | ||||||
|  |     cursor.execute( | ||||||
|  |         'INSERT INTO submission_history (student_id, assignment_id, filename, submit_date) VALUES (%s, %s, %s, NOW())', | ||||||
|  |         (student_id, assignment_id, filename) | ||||||
|  |     ) | ||||||
|  |     conn.commit() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  | 
 | ||||||
|  | # 添加或更新作业提交记录 | ||||||
|  | def add_or_update_submission(student_id, assignment_id, filename, code_verified=False): | ||||||
|  |     add_to_submission_history(student_id, assignment_id, filename)  # 保留历史记录 | ||||||
|  | 
 | ||||||
|  |     conn = get_db_connection() | ||||||
|  |     cursor = conn.cursor() | ||||||
|  |     if code_verified: | ||||||
|  |         cursor.execute( | ||||||
|  |             'UPDATE submissions SET submit_date = NOW(), filename = %s WHERE student_id = %s AND assignment_id = %s', | ||||||
|  |             (filename, student_id, assignment_id)) | ||||||
|  |     else: | ||||||
|  |         cursor.execute( | ||||||
|  |             'INSERT INTO submissions (student_id, assignment_id, filename, submit_date) VALUES (%s, %s, %s, NOW())', | ||||||
|  |             (student_id, assignment_id, filename)) | ||||||
|  |     conn.commit() | ||||||
|  |     cursor.close() | ||||||
|  |     conn.close() | ||||||
|  | 
 | ||||||
|  | # 生成预签名URL | ||||||
|  | def generate_presigned_url(object_key, content_type, expiration=3600): | ||||||
|  |     try: | ||||||
|  |         url = s3_client.generate_presigned_url( | ||||||
|  |             'put_object', | ||||||
|  |             Params={'Bucket': bucket_name, 'Key': object_key, 'ContentType': content_type}, | ||||||
|  |             ExpiresIn=expiration | ||||||
|  |         ) | ||||||
|  |         return url | ||||||
|  |     except Exception as e: | ||||||
|  |         logging.error(f"Failed to generate presigned URL: {str(e)}") | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  | # 添加管理员路由 | ||||||
|  | def add_admin_routes(app, mail, bcrypt): | ||||||
|  |     # 管理员登录装饰器 | ||||||
|  |     def admin_required(f): | ||||||
|  |         @wraps(f) | ||||||
|  |         def decorated_function(*args, **kwargs): | ||||||
|  |             if 'admin_id' not in session: | ||||||
|  |                 return redirect(url_for('admin_login')) | ||||||
|  |             return f(*args, **kwargs) | ||||||
|  |         return decorated_function | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/add_department', methods=['POST']) | ||||||
|  |     @admin_required | ||||||
|  |     def add_department(): | ||||||
|  |         name = request.form['name'] | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor() | ||||||
|  |         cursor.execute('INSERT INTO departments (name) VALUES (%s)', (name,)) | ||||||
|  |         conn.commit() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         flash('Department added successfully', 'success') | ||||||
|  |         return redirect(url_for('admin_panel')) | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/add_grade', methods=['POST']) | ||||||
|  |     @admin_required | ||||||
|  |     def add_grade(): | ||||||
|  |         year = request.form['year'] | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor() | ||||||
|  |         cursor.execute('INSERT INTO grades (year) VALUES (%s)', (year,)) | ||||||
|  |         conn.commit() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         flash('Grade added successfully', 'success') | ||||||
|  |         return redirect(url_for('admin_panel')) | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/add_class', methods=['POST']) | ||||||
|  |     @admin_required | ||||||
|  |     def add_class(): | ||||||
|  |         name = request.form['name'] | ||||||
|  |         department_id = request.form['department_id'] | ||||||
|  |         grade_id = request.form['grade_id'] | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor() | ||||||
|  |         cursor.execute('INSERT INTO classes (name, department_id, grade_id) VALUES (%s, %s, %s)', | ||||||
|  |                        (name, department_id, grade_id)) | ||||||
|  |         conn.commit() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         flash('Class added successfully', 'success') | ||||||
|  |         return redirect(url_for('admin_panel')) | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/add_teacher', methods=['POST']) | ||||||
|  |     @admin_required | ||||||
|  |     def add_teacher(): | ||||||
|  |         name = request.form['name'] | ||||||
|  |         email = request.form['email'] | ||||||
|  |         password = bcrypt.generate_password_hash(request.form['password']).decode('utf-8') | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor() | ||||||
|  |         cursor.execute('INSERT INTO teachers (name, email, password) VALUES (%s, %s, %s)', | ||||||
|  |                        (name, email, password)) | ||||||
|  |         conn.commit() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         flash('Teacher added successfully', 'success') | ||||||
|  |         return redirect(url_for('admin_panel')) | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/add_administrator', methods=['POST']) | ||||||
|  |     @admin_required | ||||||
|  |     def add_administrator(): | ||||||
|  |         username = request.form['username'] | ||||||
|  |         password = bcrypt.generate_password_hash(request.form['password']).decode('utf-8') | ||||||
|  |         teacher_id = request.form['teacher_id'] | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor() | ||||||
|  |         cursor.execute('INSERT INTO administrators (username, password, teacher_id) VALUES (%s, %s, %s)', | ||||||
|  |                        (username, password, teacher_id)) | ||||||
|  |         conn.commit() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         flash('Administrator added successfully', 'success') | ||||||
|  |         return redirect(url_for('admin_panel')) | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/assign_teacher', methods=['POST']) | ||||||
|  |     @admin_required | ||||||
|  |     def assign_teacher(): | ||||||
|  |         class_id = request.form['class_id'] | ||||||
|  |         teacher_id = request.form['teacher_id'] | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor() | ||||||
|  |         cursor.execute('INSERT INTO class_teacher (class_id, teacher_id) VALUES (%s, %s)', (class_id, teacher_id)) | ||||||
|  |         conn.commit() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         flash('Teacher assigned to class successfully', 'success') | ||||||
|  |         return redirect(url_for('admin_panel')) | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/login', methods=['GET', 'POST']) | ||||||
|  |     def admin_login(): | ||||||
|  |         if request.method == 'POST': | ||||||
|  |             username = request.form['username'] | ||||||
|  |             password = request.form['password'] | ||||||
|  |             admin = validate_admin(username, password) | ||||||
|  |             if admin: | ||||||
|  |                 session['admin_id'] = admin['id'] | ||||||
|  |                 return redirect(url_for('admin_panel')) | ||||||
|  |             else: | ||||||
|  |                 flash('Invalid credentials', 'error') | ||||||
|  |         return render_template('admin_login.html') | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/logout') | ||||||
|  |     def admin_logout(): | ||||||
|  |         session.pop('admin_id', None) | ||||||
|  |         return redirect(url_for('admin_login')) | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/panel') | ||||||
|  |     @admin_required | ||||||
|  |     def admin_panel(): | ||||||
|  |         # Retrieve necessary data (e.g., departments, grades, classes, etc.) | ||||||
|  |         departments = fetch_all_departments() | ||||||
|  |         grades = fetch_all_grades() | ||||||
|  |         classes = fetch_all_classes() | ||||||
|  |         teachers = fetch_all_teachers() | ||||||
|  |         return render_template('admin_panel.html', departments=departments, grades=grades, classes=classes, teachers=teachers) | ||||||
|  | 
 | ||||||
|  |     @app.route('/admin/add_assignment', methods=['POST']) | ||||||
|  |     @admin_required | ||||||
|  |     def admin_add_assignment(): | ||||||
|  |         value = request.form['value'] | ||||||
|  |         name = request.form['name'] | ||||||
|  |         deadline = request.form['deadline'] | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor() | ||||||
|  |         cursor.execute('INSERT INTO assignments (value, name, deadline) VALUES (%s, %s, %s)', (value, name, deadline)) | ||||||
|  |         conn.commit() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         flash('Assignment added successfully', 'success') | ||||||
|  |         return redirect(url_for('admin_panel')) | ||||||
|  | 
 | ||||||
|  |     @app.route('/login', methods=['GET', 'POST']) | ||||||
|  |     def login(): | ||||||
|  |         if request.method == 'POST': | ||||||
|  |             student_id = request.form.get('student_id') | ||||||
|  |             password = request.form.get('password') | ||||||
|  |             student = validate_student(student_id, password, bcrypt) | ||||||
|  |             if student: | ||||||
|  |                 session['student_id'] = student['id'] | ||||||
|  |                 session['student_name'] = student['name'] | ||||||
|  |                 return redirect(url_for('serve_index')) | ||||||
|  |             else: | ||||||
|  |                 return render_template('login.html', error='学号或密码错误') | ||||||
|  |         return render_template('login.html') | ||||||
|  | 
 | ||||||
|  |     @app.route('/reset-password', methods=['GET', 'POST']) | ||||||
|  |     def reset_password(): | ||||||
|  |         if request.method == 'POST': | ||||||
|  |             student_id = request.form.get('student_id') | ||||||
|  |             email = request.form.get('email') | ||||||
|  |             code = request.form.get('code') | ||||||
|  |             new_password = request.form.get('new_password') | ||||||
|  |             if code: | ||||||
|  |                 if session.get('reset_code') == code and session.get('reset_student_id') == student_id: | ||||||
|  |                     conn = get_db_connection() | ||||||
|  |                     cursor = conn.cursor() | ||||||
|  |                     hashed_password = bcrypt.generate_password_hash(new_password).decode('utf-8') | ||||||
|  |                     cursor.execute('UPDATE students SET password = %s WHERE id = %s', (hashed_password, student_id)) | ||||||
|  |                     conn.commit() | ||||||
|  |                     cursor.close() | ||||||
|  |                     conn.close() | ||||||
|  |                     return render_template('login.html', success='密码已成功重置,请使用新密码登录') | ||||||
|  |                 else: | ||||||
|  |                     return render_template('reset_password.html', error='验证码错误') | ||||||
|  |             else: | ||||||
|  |                 conn = get_db_connection() | ||||||
|  |                 cursor = conn.cursor(dictionary=True) | ||||||
|  |                 cursor.execute('SELECT * FROM students WHERE id = %s AND email = %s', (student_id, email)) | ||||||
|  |                 student = cursor.fetchone() | ||||||
|  |                 cursor.close() | ||||||
|  |                 conn.close() | ||||||
|  |                 if student: | ||||||
|  |                     reset_code = ''.join(random.choices('0123456789', k=6)) | ||||||
|  |                     session['reset_code'] = reset_code | ||||||
|  |                     session['reset_student_id'] = student_id | ||||||
|  |                     try: | ||||||
|  |                         msg = Message('重置密码验证码', recipients=[email]) | ||||||
|  |                         msg.body = f'您用于重置密码的验证码是: {reset_code}' | ||||||
|  |                         mail.send(msg) | ||||||
|  |                         return render_template('reset_password.html', | ||||||
|  |                                                success='验证码已发送到您的邮箱,请检查并输入验证码') | ||||||
|  |                     except Exception as e: | ||||||
|  |                         logging.error(f"Error sending email: {str(e)}") | ||||||
|  |                         return render_template('reset_password.html', error='发送验证码失败,请稍后再试') | ||||||
|  |                 else: | ||||||
|  |                     return render_template('reset_password.html', error='学号和邮箱不匹配') | ||||||
|  |         return render_template('reset_password.html') | ||||||
|  | 
 | ||||||
|  |     @app.route('/record-submission', methods=['POST']) | ||||||
|  |     def record_submission(): | ||||||
|  |         data = request.json | ||||||
|  |         student_id = session.get('student_id') | ||||||
|  |         student_name = session.get('student_name') | ||||||
|  |         assignment = data.get('assignment') | ||||||
|  |         filename = data.get('filename')  # 通过前端传递的文件名获取 | ||||||
|  | 
 | ||||||
|  |         if not student_id or not filename or not assignment: | ||||||
|  |             return jsonify({'error': '学号、作业和文件名是必要参数'}), 400 | ||||||
|  | 
 | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor(dictionary=True) | ||||||
|  |         cursor.execute('SELECT * FROM submissions WHERE student_id = %s AND assignment_id = %s', | ||||||
|  |                        (student_id, assignment)) | ||||||
|  |         submission = cursor.fetchone()  # 检查该学生是否已提交过此作业 | ||||||
|  | 
 | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  | 
 | ||||||
|  |         if submission: | ||||||
|  |             session['filename'] = filename  # 保留新文件名以备`validate-code`使用 | ||||||
|  |             return jsonify({'error': '作业已提交过,请输入验证码继续'}), 401  # 提示用户输入验证码以覆盖提交 | ||||||
|  | 
 | ||||||
|  |         # 提取当前上传文件的扩展名 | ||||||
|  |         _, file_extension = os.path.splitext(filename)  # 通过前端传递的文件提取扩展名 | ||||||
|  | 
 | ||||||
|  |         # 生成新的文件名,并确保文件扩展名保持正确 | ||||||
|  |         new_filename = f'{student_id}_{student_name}_{assignment}{file_extension}' | ||||||
|  |         folder_name = f'sure_homework_define_by_qin/{assignment}' | ||||||
|  |         object_key = f'{folder_name}/{new_filename}' | ||||||
|  | 
 | ||||||
|  |         # 生成预签名 URL | ||||||
|  |         url = generate_presigned_url(object_key, 'application/octet-stream') | ||||||
|  | 
 | ||||||
|  |         if not url: | ||||||
|  |             logging.error("Failed to generate presigned URL") | ||||||
|  |             return jsonify({'error': '无法生成预签名 URL'}), 500 | ||||||
|  | 
 | ||||||
|  |         # 保存提交记录 | ||||||
|  |         add_or_update_submission(student_id, assignment, new_filename, code_verified=False) | ||||||
|  | 
 | ||||||
|  |         return jsonify({'status': 'success', 'upload_url': url}) | ||||||
|  | 
 | ||||||
|  |     @app.route('/generate-code', methods=['POST']) | ||||||
|  |     def generate_code(): | ||||||
|  |         student_id = session.get('student_id') | ||||||
|  |         assignment = request.json.get('assignment') | ||||||
|  | 
 | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor(dictionary=True) | ||||||
|  |         cursor.execute('SELECT email FROM students WHERE id = %s', (student_id,)) | ||||||
|  |         student = cursor.fetchone() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  | 
 | ||||||
|  |         if not student: | ||||||
|  |             return jsonify({'error': '学生信息未找到'}), 404 | ||||||
|  | 
 | ||||||
|  |         email = student['email'] | ||||||
|  |         reset_code = ''.join(random.choices('0123456789', k=6)) | ||||||
|  |         session['submission_code'] = reset_code | ||||||
|  |         session['submission_student_id'] = student_id | ||||||
|  |         session['submission_assignment'] = assignment | ||||||
|  | 
 | ||||||
|  |         try: | ||||||
|  |             msg = Message('提交作业验证码', recipients=[email]) | ||||||
|  |             msg.body = f'您用于提交作业的验证码是: {reset_code}' | ||||||
|  |             mail.send(msg) | ||||||
|  |             return jsonify({'status': '验证码已发送到您的邮箱,请检查并输入验证码'}) | ||||||
|  |         except Exception as e: | ||||||
|  |             logging.error(f"Error sending email: {str(e)}") | ||||||
|  |             return jsonify({'error': '发送验证码失败,请稍后再试'}), 500 | ||||||
|  | 
 | ||||||
|  |     @app.route('/validate-code', methods=['POST']) | ||||||
|  |     def validate_code(): | ||||||
|  |         request_data = request.json | ||||||
|  |         student_id = session.get('student_id') | ||||||
|  |         assignment = request_data.get('assignment') | ||||||
|  |         code = request_data.get('code') | ||||||
|  | 
 | ||||||
|  |         # 验证提交的验证码与 session 中的值是否匹配 | ||||||
|  |         if (code == session.get('submission_code') and | ||||||
|  |                 student_id == session.get('submission_student_id') and | ||||||
|  |                 assignment == session.get('submission_assignment')): | ||||||
|  | 
 | ||||||
|  |             # 从 session 中获取新文件名 | ||||||
|  |             filename = session.pop('filename', None) | ||||||
|  | 
 | ||||||
|  |             if not filename: | ||||||
|  |                 return jsonify({'error': 'No file was found in session'}), 400 | ||||||
|  | 
 | ||||||
|  |             # 提取文件扩展名 | ||||||
|  |             _, file_extension = os.path.splitext(filename)  # 获取文件的后缀名 | ||||||
|  | 
 | ||||||
|  |             # 使用学号、学生名、作业名生成文件名(不含后缀) | ||||||
|  |             new_filename = f'{student_id}_{session.get("student_name")}_{assignment}{file_extension}' | ||||||
|  |             folder_name = f'sure_homework_define_by_qin/{assignment}' | ||||||
|  | 
 | ||||||
|  |             # 生成对象键前缀(不含扩展名),用于查找已存在的文件 | ||||||
|  |             object_key_prefix = f'{folder_name}/{student_id}_{session.get("student_name")}_{assignment}' | ||||||
|  | 
 | ||||||
|  |             # 删除已存在的文件(任何扩展名) | ||||||
|  |             delete_old_files_in_s3(object_key_prefix) | ||||||
|  | 
 | ||||||
|  |             # 再生成新的对象键(含有扩展名) | ||||||
|  |             object_key = f'{object_key_prefix}{file_extension}' | ||||||
|  | 
 | ||||||
|  |             logging.info(f"Generated object_key: {object_key}") | ||||||
|  | 
 | ||||||
|  |             # 生成预签名 URL | ||||||
|  |             url = generate_presigned_url(object_key, 'application/octet-stream') | ||||||
|  | 
 | ||||||
|  |             if not url: | ||||||
|  |                 logging.error("Failed to generate presigned URL") | ||||||
|  |                 return jsonify({'error': 'Failed to generate presigned URL'}), 500 | ||||||
|  | 
 | ||||||
|  |             # 更新数据库记录 | ||||||
|  |             add_or_update_submission(student_id, assignment, new_filename, code_verified=True) | ||||||
|  | 
 | ||||||
|  |             # 清除 session 中的验证码和作业信息 | ||||||
|  |             session.pop('submission_code', None) | ||||||
|  |             session.pop('submission_student_id', None) | ||||||
|  |             session.pop('submission_assignment', None) | ||||||
|  | 
 | ||||||
|  |             session['validated_assignment'] = assignment | ||||||
|  |             session['validation_presigned_url'] = url | ||||||
|  | 
 | ||||||
|  |             return jsonify({'status': '成功', 'upload_url': url}) | ||||||
|  |         else: | ||||||
|  |             return jsonify({'error': '验证码错误'}), 400 | ||||||
|  | 
 | ||||||
|  |     # 辅助函数:删除 S3 存储桶中具有指定前缀的旧文件(无论扩展名是何) | ||||||
|  |     def delete_old_files_in_s3(object_key_prefix): | ||||||
|  |         # 查找存储桶中是否已经存在具有相同前缀(不同后缀)的文件 | ||||||
|  |         response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix=object_key_prefix) | ||||||
|  | 
 | ||||||
|  |         if 'Contents' in response: | ||||||
|  |             for obj in response['Contents']: | ||||||
|  |                 s3_client.delete_object(Bucket=bucket_name, Key=obj['Key']) | ||||||
|  |                 logging.info(f"Deleted old file from S3: {obj['Key']}") | ||||||
|  | 
 | ||||||
|  |     @app.route('/api/submissions') | ||||||
|  |     def get_submissions(): | ||||||
|  |         try: | ||||||
|  |             conn = get_db_connection() | ||||||
|  |             cursor = conn.cursor(dictionary=True) | ||||||
|  |             cursor.execute(""" | ||||||
|  |                 SELECT  | ||||||
|  |                     sh.submit_date AS 时间,  | ||||||
|  |                     sh.filename AS 提交的文件,  | ||||||
|  |                     s.name AS 姓名,  | ||||||
|  |                     s.id AS 学号,  | ||||||
|  |                     sh.assignment_id AS 作业 | ||||||
|  |                 FROM submission_history sh | ||||||
|  |                 JOIN students s ON sh.student_id = s.id | ||||||
|  |                 ORDER BY sh.submit_date DESC | ||||||
|  |             """) | ||||||
|  |             submissions = cursor.fetchall() | ||||||
|  |             cursor.close() | ||||||
|  |             conn.close() | ||||||
|  | 
 | ||||||
|  |             # 格式化日期时间 | ||||||
|  |             for sub in submissions: | ||||||
|  |                 sub['时间'] = sub['时间'].strftime('%Y-%m-%d %H:%M:%S') if sub['时间'] else None | ||||||
|  | 
 | ||||||
|  |             return jsonify(submissions) | ||||||
|  |         except Exception as e: | ||||||
|  |             logging.error(f"Error in get_submissions: {str(e)}", exc_info=True) | ||||||
|  |             return jsonify({'error': str(e)}), 500 | ||||||
|  | 
 | ||||||
|  |     @app.route('/api/assignment-status') | ||||||
|  |     def get_assignment_status(): | ||||||
|  |         try: | ||||||
|  |             conn = get_db_connection() | ||||||
|  |             cursor = conn.cursor(dictionary=True) | ||||||
|  |             cursor.execute(""" | ||||||
|  |                 SELECT  | ||||||
|  |                     s.id AS 学号,  | ||||||
|  |                     s.name AS 姓名, | ||||||
|  |                     a.assignment_id AS 作业, | ||||||
|  |                     CASE WHEN sub.id IS NOT NULL THEN '已提交' ELSE '未提交' END AS 提交情况, | ||||||
|  |                     COALESCE(sub.filename, '') AS 提交的文件 | ||||||
|  |                 FROM students s | ||||||
|  |                 CROSS JOIN (SELECT DISTINCT assignment_id FROM submissions) a | ||||||
|  |                 LEFT JOIN submissions sub ON s.id = sub.student_id AND a.assignment_id = sub.assignment_id | ||||||
|  |                 ORDER BY s.id, a.assignment_id | ||||||
|  |             """) | ||||||
|  |             status_data = cursor.fetchall() | ||||||
|  |             cursor.close() | ||||||
|  |             conn.close() | ||||||
|  | 
 | ||||||
|  |             return jsonify(status_data) | ||||||
|  |         except Exception as e: | ||||||
|  |             logging.error(f"Error in get_assignment_status: {str(e)}", exc_info=True) | ||||||
|  |             return jsonify({'error': str(e)}), 500 | ||||||
|  | 
 | ||||||
|  |     @app.route('/download-submissions') | ||||||
|  |     def download_submissions(): | ||||||
|  |         try: | ||||||
|  |             conn = get_db_connection() | ||||||
|  |             cursor = conn.cursor(dictionary=True) | ||||||
|  |             cursor.execute(""" | ||||||
|  |                 SELECT  | ||||||
|  |                     sh.submit_date AS 时间,  | ||||||
|  |                     sh.filename AS 提交的项目,  | ||||||
|  |                     s.name AS 姓名,  | ||||||
|  |                     s.id AS 学号,  | ||||||
|  |                     sh.assignment_id AS 作业 | ||||||
|  |                 FROM submission_history sh | ||||||
|  |                 JOIN students s ON sh.student_id = s.id | ||||||
|  |                 ORDER BY sh.submit_date DESC | ||||||
|  |             """) | ||||||
|  |             submissions = cursor.fetchall() | ||||||
|  |             cursor.close() | ||||||
|  |             conn.close() | ||||||
|  | 
 | ||||||
|  |             # 创建 DataFrame | ||||||
|  |             df = pd.DataFrame(submissions) | ||||||
|  | 
 | ||||||
|  |             # 格式化日期时间 | ||||||
|  |             df['时间'] = pd.to_datetime(df['时间']).dt.strftime('%Y-%m-%d %H:%M:%S') | ||||||
|  | 
 | ||||||
|  |             # 确保列的顺序 | ||||||
|  |             df = df[['时间', '提交的项目', '姓名', '学号', '作业']] | ||||||
|  | 
 | ||||||
|  |             # 创建一个 BytesIO 对象,用于存储 Excel 文件 | ||||||
|  |             output = io.BytesIO() | ||||||
|  | 
 | ||||||
|  |             # 使用 ExcelWriter 来设置列宽 | ||||||
|  |             with pd.ExcelWriter(output, engine='xlsxwriter') as writer: | ||||||
|  |                 df.to_excel(writer, sheet_name='提交记录', index=False) | ||||||
|  |                 worksheet = writer.sheets['提交记录'] | ||||||
|  | 
 | ||||||
|  |                 # 设置列宽 | ||||||
|  |                 worksheet.set_column('A:A', 20)  # 时间 | ||||||
|  |                 worksheet.set_column('B:B', 30)  # 提交的项目 | ||||||
|  |                 worksheet.set_column('C:C', 15)  # 姓名 | ||||||
|  |                 worksheet.set_column('D:D', 15)  # 学号 | ||||||
|  |                 worksheet.set_column('E:E', 20)  # 作业 | ||||||
|  | 
 | ||||||
|  |             output.seek(0) | ||||||
|  | 
 | ||||||
|  |             return send_file( | ||||||
|  |                 output, | ||||||
|  |                 as_attachment=True, | ||||||
|  |                 download_name='submissions.xlsx', | ||||||
|  |                 mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | ||||||
|  |             ) | ||||||
|  |         except Exception as e: | ||||||
|  |             logging.error(f"Error in download_submissions: {str(e)}", exc_info=True) | ||||||
|  |             return jsonify({'error': str(e)}), 500 | ||||||
|  | 
 | ||||||
|  |     @app.route('/download-assignment-status') | ||||||
|  |     def download_assignment_status(): | ||||||
|  |         try: | ||||||
|  |             conn = get_db_connection() | ||||||
|  |             cursor = conn.cursor(dictionary=True) | ||||||
|  |             cursor.execute(""" | ||||||
|  |                 SELECT  | ||||||
|  |                     s.id AS 学号,  | ||||||
|  |                     s.name AS 姓名,  | ||||||
|  |                     COALESCE(sub.assignment_id, 'No assignment') AS 作业, | ||||||
|  |                     CASE WHEN sub.id IS NOT NULL THEN '已提交' ELSE '未提交' END AS 作业提交情况, | ||||||
|  |                     COALESCE(sub.filename, '') AS 提交的项目, | ||||||
|  |                     COALESCE(sub.submit_date, '') AS 提交时间 | ||||||
|  |                 FROM students s | ||||||
|  |                 LEFT JOIN submissions sub ON s.id = sub.student_id | ||||||
|  |                 ORDER BY s.id, sub.assignment_id | ||||||
|  |             """) | ||||||
|  |             status_data = cursor.fetchall() | ||||||
|  |             cursor.close() | ||||||
|  |             conn.close() | ||||||
|  | 
 | ||||||
|  |             df = pd.DataFrame(status_data) | ||||||
|  | 
 | ||||||
|  |             # 格式化日期时间 | ||||||
|  |             df['提交时间'] = pd.to_datetime(df['提交时间']).dt.strftime('%Y-%m-%d %H:%M:%S') | ||||||
|  | 
 | ||||||
|  |             # 确保列的顺序 | ||||||
|  |             df = df[['学号', '姓名', '作业', '作业提交情况', '提交的项目', '提交时间']] | ||||||
|  | 
 | ||||||
|  |             output = io.BytesIO() | ||||||
|  |             with pd.ExcelWriter(output, engine='xlsxwriter') as writer: | ||||||
|  |                 df.to_excel(writer, sheet_name='作业提交情况', index=False) | ||||||
|  |                 worksheet = writer.sheets['作业提交情况'] | ||||||
|  | 
 | ||||||
|  |                 # 设置列宽 | ||||||
|  |                 worksheet.set_column('A:A', 15)  # 学号 | ||||||
|  |                 worksheet.set_column('B:B', 10)  # 姓名 | ||||||
|  |                 worksheet.set_column('C:C', 15)  # 作业 | ||||||
|  |                 worksheet.set_column('D:D', 12)  # 作业提交情况 | ||||||
|  |                 worksheet.set_column('E:E', 30)  # 提交的项目 | ||||||
|  |                 worksheet.set_column('F:F', 20)  # 提交时间 | ||||||
|  | 
 | ||||||
|  |             output.seek(0) | ||||||
|  | 
 | ||||||
|  |             return send_file( | ||||||
|  |                 output, | ||||||
|  |                 as_attachment=True, | ||||||
|  |                 download_name='assignment_status.xlsx', | ||||||
|  |                 mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | ||||||
|  |             ) | ||||||
|  |         except Exception as e: | ||||||
|  |             logging.error(f"Error in download_assignment_status: {str(e)}", exc_info=True) | ||||||
|  |             return jsonify({'error': str(e)}), 500 | ||||||
|  | 
 | ||||||
|  |     @app.route('/') | ||||||
|  |     def serve_index(): | ||||||
|  |         if 'student_id' not in session: | ||||||
|  |             return redirect(url_for('login')) | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor(dictionary=True) | ||||||
|  |         cursor.execute('SELECT * FROM assignments ORDER BY deadline') | ||||||
|  |         assignments = cursor.fetchall() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         return render_template('index.html', assignments=assignments) | ||||||
|  | 
 | ||||||
|  |     @app.route('/logout') | ||||||
|  |     def logout(): | ||||||
|  |         session.clear() | ||||||
|  |         return redirect(url_for('login')) | ||||||
|  | 
 | ||||||
|  |     def _build_cors_preflight_response(): | ||||||
|  |         response = make_response() | ||||||
|  |         response.headers.add("Access-Control-Allow-Origin", "*") | ||||||
|  |         response.headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") | ||||||
|  |         response.headers.add("Access-Control-Allow-Headers", "Content-Type") | ||||||
|  |         return response | ||||||
|  | 
 | ||||||
|  | # 教师管理路由 | ||||||
|  | def add_teacher_routes(app, mail, bcrypt): | ||||||
|  |     # 教师登录装饰器 | ||||||
|  |     def teacher_required(f): | ||||||
|  |         @wraps(f) | ||||||
|  |         def decorated_function(*args, **kwargs): | ||||||
|  |             if 'teacher_id' not in session: | ||||||
|  |                 return redirect(url_for('teacher_login')) | ||||||
|  |             return f(*args, **kwargs) | ||||||
|  |         return decorated_function | ||||||
|  | 
 | ||||||
|  |     # Teacher Login Route | ||||||
|  |     @app.route('/teacher/login', methods=['GET', 'POST']) | ||||||
|  |     def teacher_login(): | ||||||
|  |         if request.method == 'POST': | ||||||
|  |             email = request.form['email'] | ||||||
|  |             password = request.form['password'] | ||||||
|  |             teacher = validate_teacher(email, password, bcrypt) | ||||||
|  |             if teacher: | ||||||
|  |                 session['teacher_id'] = teacher['id'] | ||||||
|  |                 return redirect(url_for('teacher_panel')) | ||||||
|  |             else: | ||||||
|  |                 flash('Invalid credentials', 'error') | ||||||
|  |         return render_template('teacher_login.html') | ||||||
|  | 
 | ||||||
|  |     @app.route('/teacher/logout') | ||||||
|  |     def teacher_logout(): | ||||||
|  |         session.pop('teacher_id', None) | ||||||
|  |         return redirect(url_for('teacher_login')) | ||||||
|  | 
 | ||||||
|  |     @app.route('/teacher/panel') | ||||||
|  |     @teacher_required | ||||||
|  |     def teacher_panel(): | ||||||
|  |         # Fetch the classes this teacher is responsible for | ||||||
|  |         teacher_id = session['teacher_id'] | ||||||
|  |         classes = fetch_teacher_classes(teacher_id) | ||||||
|  |         return render_template('teacher_panel.html', classes=classes) | ||||||
|  | 
 | ||||||
|  |     @app.route('/teacher/download-assignment/<assignment_value>', methods=['GET']) | ||||||
|  |     def download_assignment(assignment_value): | ||||||
|  |         """生成 ZIP 并直接发送给用户""" | ||||||
|  |         teacher_id = session.get('teacher_id') | ||||||
|  |         if not teacher_id: | ||||||
|  |             return redirect(url_for('teacher_login')) | ||||||
|  | 
 | ||||||
|  |         # 构造 S3 文件夹路径 | ||||||
|  |         folder_key = f"sure_homework_define_by_qin/{assignment_value}/" | ||||||
|  | 
 | ||||||
|  |         # 下载并压缩作业文件夹 | ||||||
|  |         zip_file_path = download_and_zip_files(os.getenv('S3_BUCKET_NAME'), folder_key) | ||||||
|  | 
 | ||||||
|  |         if not zip_file_path or not os.path.exists(zip_file_path): | ||||||
|  |             logging.error(f"Failed to create ZIP file at {zip_file_path}") | ||||||
|  |             return jsonify({"error": "无法创建 zip 文件"}), 500 | ||||||
|  | 
 | ||||||
|  |         try: | ||||||
|  |             # 将来自 assignment_value 的文件名进行 URL 编码,以支持中文和特殊字符 | ||||||
|  |             filename = quote(f"{assignment_value}.zip") | ||||||
|  | 
 | ||||||
|  |             logging.info(f"Sending file: {zip_file_path} as {filename}") | ||||||
|  |             return send_file(zip_file_path, as_attachment=True, download_name=filename, mimetype='application/zip') | ||||||
|  |         except Exception as ex: | ||||||
|  |             logging.error(f"Error sending file: {str(ex)}") | ||||||
|  |             return jsonify({"error": "Error sending file", "details": str(ex)}), 500 | ||||||
|  |          | ||||||
|  |     @app.route('/teacher/class/<int:class_id>') | ||||||
|  |     @teacher_required | ||||||
|  |     def view_class(class_id): | ||||||
|  |         # Fetch the assignments and students of the class | ||||||
|  |         assignments = fetch_class_assignments(class_id) | ||||||
|  |         students = fetch_class_students(class_id) | ||||||
|  |         return render_template('class_detail.html', assignments=assignments, students=students, class_id=class_id) | ||||||
|  | 
 | ||||||
|  |     @app.route('/teacher/class/<int:class_id>/add_assignment', methods=['POST']) | ||||||
|  |     @teacher_required | ||||||
|  |     def teacher_add_assignment(class_id): | ||||||
|  |         # Adding a new assignment to the class | ||||||
|  |         value = request.form['value'] | ||||||
|  |         name = request.form['name'] | ||||||
|  |         deadline = request.form['deadline'] | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor() | ||||||
|  |         cursor.execute('INSERT INTO assignments (value, name, deadline, class_id) VALUES (%s, %s, %s, %s)', (value, name, deadline, class_id)) | ||||||
|  |         conn.commit() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         flash('Assignment added successfully', 'success') | ||||||
|  |         return redirect(url_for('view_class', class_id=class_id)) | ||||||
|  | 
 | ||||||
|  |     @app.route('/teacher/edit_assignment/<int:assignment_id>', methods=['POST']) | ||||||
|  |     def edit_assignment(assignment_id): | ||||||
|  |         new_deadline = request.json.get('deadline') | ||||||
|  |         if new_deadline: | ||||||
|  |             conn = get_db_connection() | ||||||
|  |             cursor = conn.cursor() | ||||||
|  |             cursor.execute('UPDATE assignments SET deadline = %s WHERE id = %s', (new_deadline, assignment_id)) | ||||||
|  |             conn.commit() | ||||||
|  |             cursor.close() | ||||||
|  |             conn.close() | ||||||
|  |             return jsonify({'status': 'success'}) | ||||||
|  |         return jsonify({'error': 'Invalid deadline'}), 400 | ||||||
|  | 
 | ||||||
|  |     @app.route('/teacher/delete_assignment/<int:assignment_id>', methods=['DELETE']) | ||||||
|  |     def delete_assignment(assignment_id): | ||||||
|  |         conn = get_db_connection() | ||||||
|  |         cursor = conn.cursor() | ||||||
|  |         cursor.execute('DELETE FROM assignments WHERE id = %s', (assignment_id,)) | ||||||
|  |         conn.commit() | ||||||
|  |         cursor.close() | ||||||
|  |         conn.close() | ||||||
|  |         return jsonify({'status': 'success'}) | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 superlishunqin
						superlishunqin