Привет такой код есть надо укоротить код короче чтобы это было в паре файлов а не в куче файлов как щас. 📁 .git
    📁 hooks
    📁 info
    📁 logs
        📁 refs
            📁 heads
    📁 objects
        📁 04
        📁 0c
        📁 11
        📁 13
        📁 15
        📁 17
        📁 19
        📁 1b
        📁 1e
        📁 22
        📁 29
        📁 2b
        📁 2f
        📁 33
        📁 42
        📁 49
        📁 54
        📁 57
        📁 59
        📁 69
        📁 6a
        📁 74
        📁 77
        📁 7e
        📁 80
        📁 84
        📁 85
        📁 8b
        📁 8c
        📁 8f
        📁 91
        📁 a0
        📁 ae
        📁 c0
        📁 c9
        📁 cb
        📁 cd
        📁 db
        📁 e2
        📁 e6
        📁 eb
        📁 ed
        📁 f0
        📁 fc
        📁 ff
        📁 info
        📁 pack
    📁 refs
        📁 heads
        📁 tags
📁 admin
    📄 forms.py


==================== K:/Desktop/project\admin\forms.py ====================

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField, SelectField, FileField
from wtforms.validators import DataRequired, Length

class UploadForm(FlaskForm):
    file = FileField('Загрузить файл', validators=[DataRequired()])
    submit = SubmitField('Загрузить')

class AddTeacherForm(FlaskForm):
    name = StringField('ФИО преподавателя', validators=[DataRequired(), Length(min=2, max=255)])
    submit = SubmitField('Добавить')
    📄 routes.py


==================== K:/Desktop/project\admin\routes.py ====================

from flask import Blueprint, render_template, redirect, url_for, flash, request, current_app, send_file
from flask_login import current_user, login_required
from models import db, User, Discipline, Group, Student
from .forms import UploadForm, AddTeacherForm
from werkzeug.utils import secure_filename
import os
import csv
import pandas as pd
from flask_bcrypt import generate_password_hash
import uuid

admin_bp = Blueprint('admin', __name__)

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS']

@admin_bp.route('/dashboard')
@login_required
def dashboard():
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('auth.login'))
    return render_template('admin/dashboard.html')

@admin_bp.route('/upload', methods=['GET', 'POST'])
@login_required
def upload():
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('auth.login'))
    
    form = UploadForm()
    if form.validate_on_submit():
        file = form.file.data
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            unique_filename = f"{uuid.uuid4().hex}_{filename}"
            filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], unique_filename)
            file.save(filepath)
            # Определение типа файла и парсинг
            try:
                if filename.endswith('.csv'):
                    df = pd.read_csv(filepath)
                elif filename.endswith('.xlsx'):
                    df = pd.read_excel(filepath)
                else:
                    flash('Неподдерживаемый формат файла.', 'danger')
                    return redirect(request.url)
                
                # Обработка файла в зависимости от его содержания
                # Предположим, что в файле указаны группы, студенты или дисциплины
                if 'Группа' in df.columns or 'groups' in df.columns:
                    # Обработка групп
                    for index, row in df.iterrows():
                        group_name = row.get('name') or row.get('Группа') or row.get('group') 
                        if group_name and not Group.query.filter_by(name=group_name).first():
                            new_group = Group(name=group_name)
                            db.session.add(new_group)
                    db.session.commit()
                    flash('Группы успешно загружены.', 'success')
                
                elif 'Дисциплина' in df.columns or 'discipline' in df.columns:
                    # Обработка дисциплин
                    for index, row in df.iterrows():
                        discipline_name = row.get('name') or row.get('Дисциплина') or row.get('discipline') 
                        if discipline_name and not Discipline.query.filter_by(name=discipline_name).first():
                            new_discipline = Discipline(name=discipline_name)
                            db.session.add(new_discipline)
                    db.session.commit()
                    flash('Дисциплины успешно загружены.', 'success')
                
                elif 'ФИО' in df.columns or 'name' in df.columns:
                    # Обработка студентов
                    for index, row in df.iterrows():
                        student_name = row.get('name') or row.get('ФИО') or row.get('student_name')
                        group_name = row.get('group') or row.get('Группа') or row.get('group_name')
                        if student_name and group_name:
                            group = Group.query.filter_by(name=group_name).first()
                            if group and not Student.query.filter_by(name=student_name, group_id=group.id).first():
                                new_student = Student(name=student_name, group_id=group.id)
                                db.session.add(new_student)
                    db.session.commit()
                    flash('Студенты успешно загружены.', 'success')
                
                else:
                    flash('Неподдерживаемый формат содержимого файла.', 'danger')
            except Exception as e:
                flash(f'Ошибка при обработке файла: {str(e)}', 'danger')
            finally:
                os.remove(filepath)  # Удаление файла после обработки
            return redirect(url_for('admin.dashboard'))
        else:
            flash('Неподдерживаемый формат файла.', 'danger')
    
    return render_template('admin/upload.html', form=form)

@admin_bp.route('/manage_teachers', methods=['GET', 'POST'])
@login_required
def manage_teachers():
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('auth.login'))
    
    form = AddTeacherForm()
    if form.validate_on_submit():
        name = form.name.data.strip()
        if not name:
            flash('Введите корректное имя.', 'danger')
            return redirect(request.url)
        # Генерация логина и пароля
        username = name.lower().replace(' ', '.')  # Простая генерация логина
        password = uuid.uuid4().hex[:8]  # Случайный пароль из 8 символов
        
        if User.query.filter_by(username=username).first():
            flash('Преподаватель с таким логином уже существует.', 'danger')
            return redirect(request.url)
        
        new_teacher = User(username=username, name=name, role='teacher')
        new_teacher.set_password(password)
        db.session.add(new_teacher)
        db.session.commit()
        
        # Здесь можно добавить функционал отправки логина и пароля преподавателю (например, по email)
        flash(f'Преподаватель добавлен. Логин: {username}, Пароль: {password}', 'success')
        return redirect(url_for('admin.manage_teachers'))
    
    teachers = User.query.filter_by(role='teacher').all()
    return render_template('admin/manage_teachers.html', form=form, teachers=teachers)

@admin_bp.route('/delete_teacher/<int:teacher_id>', methods=['POST'])
@login_required
def delete_teacher(teacher_id):
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('auth.login'))
    
    teacher = User.query.get_or_404(teacher_id)
    if teacher.role != 'teacher':
        flash('Можно удалить только преподавателей.', 'danger')
        return redirect(url_for('admin.manage_teachers'))
    
    db.session.delete(teacher)
    db.session.commit()
    flash('Преподаватель успешно удален.', 'success')
    return redirect(url_for('admin.manage_teachers'))

@admin_bp.route('/export_attendance')
@login_required
def export_attendance():
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('auth.login'))
    
    # Пример экспорта всех данных о посещаемости в CSV
    try:
        attendance_data = db.session.query(
            Discipline.name.label('Discipline'),
            Group.name.label('Group'),
            Session.date,
            Session.time,
            Student.name.label('Student'),
            Attendance.status
        ).join(Object, Object.discipline_id == Discipline.id)\
         .join(Session, Session.object_id == Object.id)\
         .join(Attendance, Attendance.session_id == Session.id)\
         .join(Student, Student.id == Attendance.student_id)\
         .join(Group, Group.id == Student.group_id)\
         .all()
        
        # Преобразование в DataFrame
        df = pd.DataFrame(attendance_data, columns=['Discipline', 'Group', 'Date', 'Time', 'Student', 'Status'])
        
        # Сохранение в CSV
        export_path = os.path.join(current_app.config['UPLOAD_FOLDER'], 'attendance_export.csv')
        df.to_csv(export_path, index=False)
        
        return send_file(export_path, as_attachment=True)
    except Exception as e:
        flash(f'Ошибка при экспорте данных: {str(e)}', 'danger')
        return redirect(url_for('admin.dashboard'))
    📄 __init__.py


==================== K:/Desktop/project\admin\__init__.py ====================


    📁 __pycache__
📄 app.py


==================== K:/Desktop/project\app.py ====================

from flask import Flask, render_template, redirect, url_for, flash, request, send_file
from config import Config
from models import User, db, bcrypt
from flask_migrate import Migrate
from auth.routes import auth_bp
from admin.routes import admin_bp
from teacher.routes import teacher_bp
from flask_login import LoginManager
import os

def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)
    
    # Инициализация расширений
    db.init_app(app)
    bcrypt.init_app(app)
    migrate = Migrate(app, db)
    
    login_manager = LoginManager()
    login_manager.login_view = 'auth.login'
    login_manager.init_app(app)
    
    @login_manager.user_loader
    def load_user(user_id):
        from models import User
        return User.query.get(int(user_id))
    
    # Регистрация Blueprints
    app.register_blueprint(auth_bp)
    app.register_blueprint(admin_bp, url_prefix='/admin')
    app.register_blueprint(teacher_bp, url_prefix='/teacher')
    
    # Создание папки для загрузок, если ее нет
    if not os.path.exists(app.config['UPLOAD_FOLDER']):
        os.makedirs(app.config['UPLOAD_FOLDER'])
    
    return app

app = create_app()

def create_admin():
    with app.app_context():
        if not User.query.filter_by(username='admin').first():
            admin = User(username='admin', name='Администратор', role='admin')
            admin.set_password('adminpassword')  # Установите на# app.py

from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from flask_bcrypt import Bcrypt
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField, SelectField, FileField, BooleanField, FieldList, FormField
from wtforms.validators import DataRequired, Length
from flask_migrate import Migrate
from werkzeug.utils import secure_filename
import os
import uuid
import pandas as pd
import datetime

# Инициализация приложения и расширений
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'  # Замените на случайный ключ
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data/students.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['UPLOAD_FOLDER'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data', 'uploads')
app.config['ALLOWED_EXTENSIONS'] = {'csv', 'xlsx'}

db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
migrate = Migrate(app, db)

login_manager = LoginManager(app)
login_manager.login_view = 'login'

# Создание папки для загрузок, если ее нет
if not os.path.exists(app.config['UPLOAD_FOLDER']):
    os.makedirs(app.config['UPLOAD_FOLDER'])

# Модели
class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(150), unique=True, nullable=False)
    password_hash = db.Column(db.String(128), nullable=False)
    name = db.Column(db.String(255), nullable=False)
    role = db.Column(db.String(50), nullable=False)  # 'admin' или 'teacher'

    def set_password(self, password):
        self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')

    def check_password(self, password):
        return bcrypt.check_password_hash(self.password_hash, password)

class Discipline(db.Model):
    __tablename__ = 'disciplines'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False, unique=True)
    objects = db.relationship('Object', backref='discipline', lazy=True)

class Group(db.Model):
    __tablename__ = 'groups'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False, unique=True)
    students = db.relationship('Student', backref='group', lazy=True)
    objects = db.relationship('Object', backref='group', lazy=True)

class Student(db.Model):
    __tablename__ = 'students'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    group_id = db.Column(db.Integer, db.ForeignKey('groups.id'), nullable=False)
    attendance = db.relationship('Attendance', backref='student', lazy=True)

class Object(db.Model):
    __tablename__ = 'objects'
    id = db.Column(db.Integer, primary_key=True)
    teacher_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    discipline_id = db.Column(db.Integer, db.ForeignKey('disciplines.id'), nullable=False)
    group_id = db.Column(db.Integer, db.ForeignKey('groups.id'), nullable=False)
    sessions = db.relationship('Session', backref='object', lazy=True)

class Session(db.Model):
    __tablename__ = 'sessions'
    id = db.Column(db.Integer, primary_key=True)
    object_id = db.Column(db.Integer, db.ForeignKey('objects.id'), nullable=False)
    date = db.Column(db.Date, nullable=False)
    time = db.Column(db.Time, nullable=True)
    attendance = db.relationship('Attendance', backref='session', lazy=True)

class Attendance(db.Model):
    __tablename__ = 'attendance'
    id = db.Column(db.Integer, primary_key=True)
    session_id = db.Column(db.Integer, db.ForeignKey('sessions.id'), nullable=False)
    student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
    status = db.Column(db.String(50), nullable=False)  # 'present' или 'absent'

# Загрузка пользователя
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

# Формы
class LoginForm(FlaskForm):
    username = StringField('Логин', validators=[DataRequired()])
    password = PasswordField('Пароль', validators=[DataRequired()])
    submit = SubmitField('Войти')

class UploadForm(FlaskForm):
    file = FileField('Загрузить файл', validators=[DataRequired()])
    submit = SubmitField('Загрузить')

class AddTeacherForm(FlaskForm):
    name = StringField('ФИО преподавателя', validators=[DataRequired(), Length(min=2, max=255)])
    submit = SubmitField('Добавить')

class CreateObjectForm(FlaskForm):
    discipline = SelectField('Дисциплина', coerce=int, validators=[DataRequired()])
    group = SelectField('Группа', coerce=int, validators=[DataRequired()])
    submit = SubmitField('Создать объект')

class AttendanceForm(FlaskForm):
    student_id = SelectField('Студент', coerce=int)
    present = BooleanField('Присутствует')

class MarkAttendanceForm(FlaskForm):
    date = SelectField('Дата', choices=[], validators=[DataRequired()])
    time = SelectField('Время', choices=[], validators=[DataRequired()])
    attendances = FieldList(FormField(AttendanceForm))
    submit = SubmitField('Сохранить посещаемость')

# Вспомогательные функции
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']

# Маршруты для авторизации
@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user and user.check_password(form.password.data):
            login_user(user)
            next_page = request.args.get('next')
            if user.role == 'admin':
                return redirect(next_page or url_for('admin_dashboard'))
            else:
                return redirect(next_page or url_for('teacher_dashboard'))
        else:
            flash('Неверный логин или пароль.', 'danger')
    return render_template('login.html', form=form)

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))

# Админские маршруты
@app.route('/admin/dashboard')
@login_required
def admin_dashboard():
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('login'))
    return render_template('admin/dashboard.html')

@app.route('/admin/upload', methods=['GET', 'POST'])
@login_required
def admin_upload():
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('login'))
    
    form = UploadForm()
    if form.validate_on_submit():
        file = form.file.data
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            unique_filename = f"{uuid.uuid4().hex}_{filename}"
            filepath = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
            file.save(filepath)
            # Обработка файла
            try:
                if filename.endswith('.csv'):
                    df = pd.read_csv(filepath)
                elif filename.endswith('.xlsx'):
                    df = pd.read_excel(filepath)
                else:
                    flash('Неподдерживаемый формат файла.', 'danger')
                    return redirect(request.url)
                
                # Пример обработки (загрузка групп, дисциплин, студентов)
                if 'Группа' in df.columns or 'groups' in df.columns:
                    for index, row in df.iterrows():
                        group_name = row.get('name') or row.get('Группа') or row.get('group') 
                        if group_name and not Group.query.filter_by(name=group_name).first():
                            new_group = Group(name=group_name)
                            db.session.add(new_group)
                    db.session.commit()
                    flash('Группы успешно загружены.', 'success')
                
                elif 'Дисциплина' in df.columns or 'discipline' in df.columns:
                    for index, row in df.iterrows():
                        discipline_name = row.get('name') or row.get('Дисциплина') or row.get('discipline') 
                        if discipline_name and not Discipline.query.filter_by(name=discipline_name).first():
                            new_discipline = Discipline(name=discipline_name)
                            db.session.add(new_discipline)
                    db.session.commit()
                    flash('Дисциплины успешно загружены.', 'success')
                
                elif 'ФИО' in df.columns or 'name' in df.columns:
                    for index, row in df.iterrows():
                        student_name = row.get('name') or row.get('ФИО') or row.get('student_name')
                        group_name = row.get('group') or row.get('Группа') or row.get('group_name')
                        if student_name and group_name:
                            group = Group.query.filter_by(name=group_name).first()
                            if group and not Student.query.filter_by(name=student_name, group_id=group.id).first():
                                new_student = Student(name=student_name, group_id=group.id)
                                db.session.add(new_student)
                    db.session.commit()
                    flash('Студенты успешно загружены.', 'success')
                
                else:
                    flash('Неподдерживаемый формат содержимого файла.', 'danger')
            except Exception as e:
                flash(f'Ошибка при обработке файла: {str(e)}', 'danger')
            finally:
                os.remove(filepath)  # Удаление файла после обработки
            return redirect(url_for('admin_dashboard'))
        else:
            flash('Неподдерживаемый формат файла.', 'danger')
    
    return render_template('admin/upload.html', form=form)

@app.route('/admin/manage_teachers', methods=['GET', 'POST'])
@login_required
def manage_teachers():
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('login'))
    
    form = AddTeacherForm()
    if form.validate_on_submit():
        name = form.name.data.strip()
        if not name:
            flash('Введите корректное имя.', 'danger')
            return redirect(request.url)
        # Генерация логина и пароля
        username = name.lower().replace(' ', '.')  # Простая генерация логина
        password = uuid.uuid4().hex[:8]  # Случайный пароль из 8 символов
        
        if User.query.filter_by(username=username).first():
            flash('Преподаватель с таким логином уже существует.', 'danger')
            return redirect(request.url)
        
        new_teacher = User(username=username, name=name, role='teacher')
        new_teacher.set_password(password)
        db.session.add(new_teacher)
        db.session.commit()
        
        # Здесь можно добавить функционал отправки логина и пароля преподавателю (например, по email)
        flash(f'Преподаватель добавлен. Логин: {username}, Пароль: {password}', 'success')
        return redirect(url_for('manage_teachers'))
    
    teachers = User.query.filter_by(role='teacher').all()
    return render_template('admin/manage_teachers.html', form=form, teachers=teachers)

@app.route('/admin/delete_teacher/<int:teacher_id>', methods=['POST'])
@login_required
def delete_teacher(teacher_id):
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('login'))
    
    teacher = User.query.get_or_404(teacher_id)
    if teacher.role != 'teacher':
        flash('Можно удалить только преподавателей.', 'danger')
        return redirect(url_for('manage_teachers'))
    
    db.session.delete(teacher)
    db.session.commit()
    flash('Преподаватель успешно удален.', 'success')
    return redirect(url_for('manage_teachers'))

@app.route('/admin/export_attendance')
@login_required
def export_attendance():
    if current_user.role != 'admin':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('login'))
    
    try:
        attendance_data = db.session.query(
            Discipline.name.label('Discipline'),
            Group.name.label('Group'),
            Session.date,
            Session.time,
            Student.name.label('Student'),
            Attendance.status
        ).join(Object, Object.discipline_id == Discipline.id)\
         .join(Session, Session.object_id == Object.id)\
         .join(Attendance, Attendance.session_id == Session.id)\
         .join(Student, Student.id == Attendance.student_id)\
         .join(Group, Group.id == Student.group_id)\
         .all()
        
        # Преобразование в DataFrame
        df = pd.DataFrame(attendance_data, columns=['Discipline', 'Group', 'Date', 'Time', 'Student', 'Status'])
        
        # Сохранение в CSV
        export_path = os.path.join(app.config['UPLOAD_FOLDER'], 'attendance_export.csv')
        df.to_csv(export_path, index=False)
        
        return send_file(export_path, as_attachment=True)
    except Exception as e:
        flash(f'Ошибка при экспорте данных: {str(e)}', 'danger')
        return redirect(url_for('admin_dashboard'))

# Маршруты для преподавателей
@app.route('/teacher/dashboard')
@login_required
def teacher_dashboard():
    if current_user.role != 'teacher':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('login'))
    objects = Object.query.filter_by(teacher_id=current_user.id).all()
    return render_template('teacher/dashboard.html', objects=objects)

@app.route('/teacher/create_object', methods=['GET', 'POST'])
@login_required
def create_object():
    if current_user.role != 'teacher':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('login'))
    
    form = CreateObjectForm()
    form.discipline.choices = [(d.id, d.name) for d in Discipline.query.order_by('name')]
    form.group.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
    
    if form.validate_on_submit():
        discipline_id = form.discipline.data
        group_id = form.group.data
        
        # Проверка на существование объекта
        existing_object = Object.query.filter_by(teacher_id=current_user.id, discipline_id=discipline_id, group_id=group_id).first()
        if existing_object:
            flash('Этот объект уже существует.', 'warning')
            return redirect(url_for('teacher_dashboard'))
        
        new_object = Object(teacher_id=current_user.id, discipline_id=discipline_id, group_id=group_id)
        db.session.add(new_object)
        db.session.commit()
        flash('Объект успешно создан.', 'success')
        return redirect(url_for('teacher_dashboard'))
    
    return render_template('teacher/create_object.html', form=form)

@app.route('/teacher/object/<int:object_id>/attendance', methods=['GET', 'POST'])
@login_required
def mark_attendance(object_id):
    if current_user.role != 'teacher':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('login'))
    
    object_ = Object.query.filter_by(id=object_id, teacher_id=current_user.id).first_or_404()
    
    form = MarkAttendanceForm()
    # Заполнение дат и времен занятий
    existing_sessions = Session.query.filter_by(object_id=object_id).all()
    form.date.choices = [(s.date.strftime('%Y-%m-%d'), s.date.strftime('%Y-%m-%d')) for s in existing_sessions]
    form.time.choices = [(s.time.strftime('%H:%M'), s.time.strftime('%H:%M')) for s in existing_sessions if s.time]
    
    students = Student.query.filter_by(group_id=object_.group_id).all()
    if request.method == 'GET':
        for student in students:
            form.attendances.append_entry({'student_id': student.id, 'present': False})
    
    if form.validate_on_submit():
        selected_date_str = form.date.data
        selected_time_str = form.time.data
        selected_date = datetime.datetime.strptime(selected_date_str, '%Y-%m-%d').date()
        selected_time = datetime.datetime.strptime(selected_time_str, '%H:%M').time() if selected_time_str else None
        
        # Проверка существования занятия
        session = Session.query.filter_by(object_id=object_id, date=selected_date, time=selected_time).first()
        if not session:
            session = Session(object_id=object_id, date=selected_date, time=selected_time)
            db.session.add(session)
            db.session.commit()
        
        # Обновление посещаемости
        for attendance_form in form.attendances.entries:
            student_id = attendance_form.form.student_id.data
            status = 'present' if attendance_form.form.present.data else 'absent'
            attendance_record = Attendance.query.filter_by(session_id=session.id, student_id=student_id).first()
            if attendance_record:
                attendance_record.status = status
            else:
                attendance_record = Attendance(session_id=session.id, student_id=student_id, status=status)
                db.session.add(attendance_record)
        db.session.commit()
        flash('Посещаемость успешно сохранена.', 'success')
        return redirect(url_for('teacher_dashboard'))
    
    return render_template('teacher/mark_attendance.html', object_=object_, form=form)

# Функция для создания администратора
def create_admin():
    with app.app_context():
        if not User.query.filter_by(username='admin').first():
            admin = User(username='admin', name='Администратор', role='admin')
            admin.set_password('adminpassword')  # Установите надежный пароль
            db.session.add(admin)
            db.session.commit()
            print('Создан администратор: admin/adminpassword')

# Запуск приложения
if __name__ == '__main__':
    create_admin()
    app.run(debug=True)

📁 auth
    📄 forms.py


==================== K:/Desktop/project\auth\forms.py ====================

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired

class LoginForm(FlaskForm):
    username = StringField('Логин', validators=[DataRequired()])
    password = PasswordField('Пароль', validators=[DataRequired()])
    submit = SubmitField('Войти')
    📄 routes.py


==================== K:/Desktop/project\auth\routes.py ====================

from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user
from models import User
from werkzeug.security import check_password_hash
from .forms import LoginForm

auth_bp = Blueprint('auth', __name__)

@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user and user.check_password(form.password.data):
            login_user(user)
            next_page = request.args.get('next')
            if user.role == 'admin':
                return redirect(next_page or url_for('admin.dashboard'))
            else:
                return redirect(next_page or url_for('teacher.dashboard'))
        else:
            flash('Неверный логин или пароль.', 'danger')
    return render_template('login.html', form=form)

@auth_bp.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('auth.login'))
    📄 __init__.py


==================== K:/Desktop/project\auth\__init__.py ====================


    📁 __pycache__
📄 config.py


==================== K:/Desktop/project\config.py ====================

import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'your-secret-key'  # Замените на случайный ключ
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///data/students.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    UPLOAD_FOLDER = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data', 'uploads')
    ALLOWED_EXTENSIONS = {'csv', 'xlsx'}
📁 data
    📁 uploads
📁 instance
📄 models.py


==================== K:/Desktop/project\models.py ====================

from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from flask_bcrypt import Bcrypt
from datetime import datetime

db = SQLAlchemy()
bcrypt = Bcrypt()

# Модель пользователя (Наследуется как от Admin, так и от Teacher)
class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(150), unique=True, nullable=False)
    password_hash = db.Column(db.String(128), nullable=False)
    name = db.Column(db.String(255), nullable=False)
    role = db.Column(db.String(50), nullable=False)  # 'admin' или 'teacher'

    def set_password(self, password):
        self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')

    def check_password(self, password):
        return bcrypt.check_password_hash(self.password_hash, password)

# Модель дисциплин
class Discipline(db.Model):
    __tablename__ = 'disciplines'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False, unique=True)
    objects = db.relationship('Object', backref='discipline', lazy=True)

# Модель групп
class Group(db.Model):
    __tablename__ = 'groups'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False, unique=True)
    students = db.relationship('Student', backref='group', lazy=True)
    objects = db.relationship('Object', backref='group', lazy=True)

# Модель студентов
class Student(db.Model):
    __tablename__ = 'students'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    group_id = db.Column(db.Integer, db.ForeignKey('groups.id'), nullable=False)
    attendance = db.relationship('Attendance', backref='student', lazy=True)

# Модель объектов преподавателя (дисциплина + группа)
class Object(db.Model):
    __tablename__ = 'objects'
    id = db.Column(db.Integer, primary_key=True)
    teacher_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    discipline_id = db.Column(db.Integer, db.ForeignKey('disciplines.id'), nullable=False)
    group_id = db.Column(db.Integer, db.ForeignKey('groups.id'), nullable=False)
    sessions = db.relationship('Session', backref='object', lazy=True)

# Модель занятий
class Session(db.Model):
    __tablename__ = 'sessions'
    id = db.Column(db.Integer, primary_key=True)
    object_id = db.Column(db.Integer, db.ForeignKey('objects.id'), nullable=False)
    date = db.Column(db.Date, nullable=False)
    time = db.Column(db.Time, nullable=True)
    attendance = db.relationship('Attendance', backref='session', lazy=True)

# Модель посещаемости
class Attendance(db.Model):
    __tablename__ = 'attendance'
    id = db.Column(db.Integer, primary_key=True)
    session_id = db.Column(db.Integer, db.ForeignKey('sessions.id'), nullable=False)
    student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
    status = db.Column(db.String(50), nullable=False)  # 'present' или 'absent'
📁 teacher
    📄 forms.py


==================== K:/Desktop/project\teacher\forms.py ====================

from flask_wtf import FlaskForm
from wtforms import SelectField, SubmitField, FormField, FieldList, BooleanField
from wtforms.validators import DataRequired
from models import Discipline, Group

class CreateObjectForm(FlaskForm):
    discipline = SelectField('Дисциплина', coerce=int, validators=[DataRequired()])
    group = SelectField('Группа', coerce=int, validators=[DataRequired()])
    submit = SubmitField('Создать объект')

class AttendanceForm(FlaskForm):
    student_id = SelectField('Студент', coerce=int)
    present = BooleanField('Присутствует')

class MarkAttendanceForm(FlaskForm):
    date = SelectField('Дата', choices=[], validators=[DataRequired()])
    time = SelectField('Время', choices=[], validators=[DataRequired()])
    attendances = FieldList(FormField(AttendanceForm))
    submit = SubmitField('Сохранить посещаемость')
    📄 routes.py


==================== K:/Desktop/project\teacher\routes.py ====================

from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import current_user, login_required
from models import db, Discipline, Group, Object, Session, Student, Attendance
from .forms import CreateObjectForm, MarkAttendanceForm
from datetime import datetime
from sqlalchemy.exc import IntegrityError

teacher_bp = Blueprint('teacher', __name__)

@teacher_bp.route('/dashboard')
@login_required
def dashboard():
    if current_user.role != 'teacher':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('auth.login'))
    objects = Object.query.filter_by(teacher_id=current_user.id).all()
    return render_template('teacher/dashboard.html', objects=objects)

@teacher_bp.route('/create_object', methods=['GET', 'POST'])
@login_required
def create_object():
    if current_user.role != 'teacher':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('auth.login'))
    
    form = CreateObjectForm()
    form.discipline.choices = [(d.id, d.name) for d in Discipline.query.order_by('name')]
    form.group.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
    
    if form.validate_on_submit():
        discipline_id = form.discipline.data
        group_id = form.group.data
        
        # Проверка на существование объекта
        existing_object = Object.query.filter_by(teacher_id=current_user.id, discipline_id=discipline_id, group_id=group_id).first()
        if existing_object:
            flash('Этот объект уже существует.', 'warning')
            return redirect(url_for('teacher.dashboard'))
        
        new_object = Object(teacher_id=current_user.id, discipline_id=discipline_id, group_id=group_id)
        db.session.add(new_object)
        db.session.commit()
        flash('Объект успешно создан.', 'success')
        return redirect(url_for('teacher.dashboard'))
    
    return render_template('teacher/create_object.html', form=form)

@teacher_bp.route('/object/<int:object_id>/attendance', methods=['GET', 'POST'])
@login_required
def mark_attendance(object_id):
    if current_user.role != 'teacher':
        flash('Доступ запрещен.', 'danger')
        return redirect(url_for('auth.login'))
    
    object_ = Object.query.filter_by(id=object_id, teacher_id=current_user.id).first_or_404()
    
    form = MarkAttendanceForm()
    # Пример заполнения дат и времен занятий
    # Здесь можно реализовать более сложное расписание
    existing_sessions = Session.query.filter_by(object_id=object_id).all()
    form.date.choices = [(s.date.strftime('%Y-%m-%d'), s.date.strftime('%Y-%m-%d')) for s in existing_sessions]
    form.time.choices = [(s.time.strftime('%H:%M'), s.time.strftime('%H:%M')) for s in existing_sessions if s.time]
    
    students = Student.query.filter_by(group_id=object_.group_id).all()
    if request.method == 'GET':
        # Инициализация формы с поля FIO
        for student in students:
            form.attendances.append_entry({'student_id': student.id, 'present': False})
    
    if form.validate_on_submit():
        selected_date_str = form.date.data
        selected_time_str = form.time.data
        selected_date = datetime.strptime(selected_date_str, '%Y-%m-%d').date()
        selected_time = datetime.strptime(selected_time_str, '%H:%M').time() if selected_time_str else None
        
        # Проверка существования занятия
        session = Session.query.filter_by(object_id=object_id, date=selected_date, time=selected_time).first()
        if not session:
            session = Session(object_id=object_id, date=selected_date, time=selected_time)
            db.session.add(session)
            db.session.commit()
        
        # Обновление посещаемости
        for attendance_form in form.attendances.entries:
            student_id = attendance_form.form.student_id.data
            status = 'present' if attendance_form.form.present.data else 'absent'
            attendance_record = Attendance.query.filter_by(session_id=session.id, student_id=student_id).first()
            if attendance_record:
                attendance_record.status = status
            else:
                attendance_record = Attendance(session_id=session.id, student_id=student_id, status=status)
                db.session.add(attendance_record)
        db.session.commit()
        flash('Посещаемость успешно сохранена.', 'success')
        return redirect(url_for('teacher.dashboard'))
    
    return render_template('teacher/mark_attendance.html', object_=object_, form=form)
    📄 __init__.py


==================== K:/Desktop/project\teacher\__init__.py ====================


    📁 __pycache__
📁 templates
    📁 admin
        📄 dashboard.html


==================== K:/Desktop/project\templates\admin\dashboard.html ====================

{% extends "base.html" %}

{% block title %}Панель Администратора{% endblock %}

{% block content %}
<h2>Панель Администратора</h2>
<ul class="list-group">
    <li class="list-group-item">
        <a href="{{ url_for('admin.upload') }}">Загрузить данные</a>
    </li>
    <li class="list-group-item">
        <a href="{{ url_for('admin.manage_teachers') }}">Управление Преподавателями</a>
    </li>
    <li class="list-group-item">
        <a href="{{ url_for('admin.export_attendance') }}">Экспорт Посещаемости</a>
    </li>
</ul>
{% endblock %}
        📄 manage_teachers.html


==================== K:/Desktop/project\templates\admin\manage_teachers.html ====================

{% extends "base.html" %}

{% block title %}Управление Преподавателями{% endblock %}

{% block content %}
<h2>Управление Преподавателями</h2>

<!-- Форма добавления нового преподавателя -->
<div class="card mb-4">
    <div class="card-header">Добавить Преподавателя</div>
    <div class="card-body">
        <form method="POST">
            {{ form.hidden_tag() }}
            <div class="form-group">
                {{ form.name.label(class="form-control-label") }}
                {{ form.name(class="form-control") }}
            </div>
            <div class="form-group">
                {{ form.submit(class="btn btn-success") }}
            </div>
        </form>
    </div>
</div>

<!-- Список преподавателей -->
<h3>Список Преподавателей</h3>
<table class="table table-bordered">
    <thead>
        <tr>
            <th>ФИО</th>
            <th>Логин</th>
            <th>Действия</th>
        </tr>
    </thead>
    <tbody>
        {% for teacher in teachers %}
            <tr>
                <td>{{ teacher.name }}</td>
                <td>{{ teacher.username }}</td>
                <td>
                    <!-- Кнопка удаления преподавателя -->
                    <form method="POST" action="{{ url_for('admin.delete_teacher', teacher_id=teacher.id) }}" onsubmit="return confirm('Вы уверены, что хотите удалить этого преподавателя?');">
                        <button type="submit" class="btn btn-danger btn-sm">Удалить</button>
                    </form>
                </td>
            </tr>
        {% endfor %}
    </tbody>
</table>
{% endblock %}
        📄 upload.html


==================== K:/Desktop/project\templates\admin\upload.html ====================

{% extends "base.html" %}

{% block title %}Загрузка Данных{% endblock %}

{% block content %}
<h2>Загрузка Данных</h2>
<form method="POST" enctype="multipart/form-data">
    {{ form.hidden_tag() }}
    <div class="form-group">
        {{ form.file.label(class="form-control-label") }}
        {{ form.file(class="form-control-file") }}
    </div>
    <div class="form-group">
        {{ form.submit(class="btn btn-primary") }}
    </div>
</form>
{% endblock %}
    📁 auth
    📄 base.html


==================== K:/Desktop/project\templates\base.html ====================

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Система Управления Посещаемостью{% endblock %}</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
    {% block head %}{% endblock %}
</head>
<body>
    {% with messages = get_flashed_messages(with_categories=true) %}
      {% if messages %}
        <div class="container mt-3">
          {% for category, message in messages %}
            <div class="alert alert-{{ category }}" role="alert">
              {{ message }}
            </div>
          {% endfor %}
        </div>
      {% endif %}
    {% endwith %}
    
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="#">Управление Посещаемостью</a>
        <div class="collapse navbar-collapse">
            <ul class="navbar-nav mr-auto">
                {% if current_user.is_authenticated %}
                    {% if current_user.role == 'admin' %}
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('admin.dashboard') }}">Панель Администратора</a>
                        </li>
                    {% elif current_user.role == 'teacher' %}
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('teacher.dashboard') }}">Личный Кабинет</a>
                        </li>
                    {% endif %}
                {% endif %}
            </ul>
            <ul class="navbar-nav">
                {% if current_user.is_authenticated %}
                    <li class="nav-item">
                        <span class="navbar-text mr-3">Привет, {{ current_user.name }}!</span>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('auth.logout') }}">Выйти</a>
                    </li>
                {% else %}
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('auth.login') }}">Войти</a>
                    </li>
                {% endif %}
            </ul>
        </div>
    </nav>
    
    <div class="container mt-4">
        {% block content %}{% endblock %}
    </div>
    
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.bundle.min.js"></script>
    {% block scripts %}{% endblock %}
</body>
</html>
    📄 login.html


==================== K:/Desktop/project\templates\login.html ====================

{% extends "base.html" %}

{% block title %}Авторизация{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-4">
        <h2 class="text-center">Вход в систему</h2>
        <form method="POST">
            {{ form.hidden_tag() }}
            <div class="form-group">
                {{ form.username.label(class="form-control-label") }}
                {{ form.username(class="form-control") }}
            </div>
            <div class="form-group">
                {{ form.password.label(class="form-control-label") }}
                {{ form.password(class="form-control") }}
            </div>
            <div class="form-group text-center">
                {{ form.submit(class="btn btn-primary btn-block") }}
            </div>
        </form>
    </div>
</div>
{% endblock %}
    📁 teacher
        📄 create_object.html


==================== K:/Desktop/project\templates\teacher\create_object.html ====================

{% extends "base.html" %}

{% block title %}Создать Новый Объект{% endblock %}

{% block content %}
<h2>Создать Новый Объект</h2>
<form method="POST">
    {{ form.hidden_tag() }}
    <div class="form-group">
        {{ form.discipline.label(class="form-control-label") }}
        {{ form.discipline(class="form-control") }}
    </div>
    <div class="form-group">
        {{ form.group.label(class="form-control-label") }}
        {{ form.group(class="form-control") }}
    </div>
    <div class="form-group">
        {{ form.submit(class="btn btn-primary") }}
    </div>
</form>
{% endblock %}
        📄 dashboard.html


==================== K:/Desktop/project\templates\teacher\dashboard.html ====================

{% extends "base.html" %}

{% block title %}Личный Кабинет Преподавателя{% endblock %}

{% block content %}
<h2>Личный Кабинет Преподавателя</h2>
<a href="{{ url_for('teacher.create_object') }}" class="btn btn-primary mb-3">Создать Новый Объект</a>

<h3>Мои Объекты</h3>
{% if objects %}
    <table class="table table-bordered">
        <thead>
            <tr>
                <th>Дисциплина</th>
                <th>Группа</th>
                <th>Действия</th>
            </tr>
        </thead>
        <tbody>
            {% for obj in objects %}
                <tr>
                    <td>{{ obj.discipline.name }}</td>
                    <td>{{ obj.group.name }}</td>
                    <td>
                        <a href="{{ url_for('teacher.mark_attendance', object_id=obj.id) }}" class="btn btn-success btn-sm">Отметить Посещаемость</a>
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
{% else %}
    <p>У вас пока нет объектов. Создайте новый объект.</p>
{% endif %}
{% endblock %}
        📄 mark_attendance.html


==================== K:/Desktop/project\templates\teacher\mark_attendance.html ====================

{% extends "base.html" %}

{% block title %}Отметить Посещаемость{% endblock %}

{% block content %}
<h2>Отметить Посещаемость</h2>
<h4>Дисциплина: {{ object_.discipline.name }}</h4>
<h4>Группа: {{ object_.group.name }}</h4>

<form method="POST">
    {{ form.hidden_tag() }}
    
    <div class="form-group">
        {{ form.date.label(class="form-control-label") }}
        {{ form.date(class="form-control") }}
    </div>
    <div class="form-group">
        {{ form.time.label(class="form-control-label") }}
        {{ form.time(class="form-control") }}
    </div>
    
    <h5>Студенты:</h5>
    <table class="table table-bordered">
        <thead>
            <tr>
                <th>ФИО</th>
                <th>Присутствует</th>
            </tr>
        </thead>
        <tbody>
            {% for attendance_form in form.attendances %}
                <tr>
                    <td>
                        <!-- Выводим имя студента -->
                        {% set student = attendance_form.form.student_id.choices | selectattr(0, 'equalto', attendance_form.form.student_id.data) | first %}
                        {{ student[1] }}
                        <!-- Скрытое поле для хранения ID студента -->
                        {{ attendance_form.form.student_id(value=attendance_form.form.student_id.data, type="hidden") }}
                    </td>
                    <td>
                        {{ attendance_form.form.present() }}
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
    
    <div class="form-group">
        {{ form.submit(class="btn btn-primary") }}
    </div>
</form>
{% endblock %}
📁 __pycache__