231 lines
8.8 KiB
Python
231 lines
8.8 KiB
Python
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
|
from flask_login import login_required, current_user
|
|
from datetime import datetime, date
|
|
from app.models.classroom import Classroom
|
|
from app.models.user import User
|
|
from app.forms.classroom import ClassroomForm
|
|
from app import db
|
|
from sqlalchemy import text
|
|
|
|
classrooms_bp = Blueprint('classrooms', __name__)
|
|
|
|
@classrooms_bp.route('/add', methods=['GET', 'POST'])
|
|
@login_required
|
|
def add_classroom():
|
|
"""Add a new classroom"""
|
|
form = ClassroomForm()
|
|
|
|
# TODO: Update when campus model is implemented
|
|
# form.campus_id.choices = [(1, 'Main Campus')]
|
|
|
|
if form.validate_on_submit():
|
|
try:
|
|
existing_classroom = Classroom.query.filter_by(
|
|
building=form.building.data,
|
|
code=form.room_number.data,
|
|
floor=form.floor.data
|
|
).first()
|
|
|
|
if existing_classroom:
|
|
flash('Classroom with this building, room number, and floor already exists.', 'error')
|
|
return render_template('classrooms/add.html', form=form, title='Add Classroom')
|
|
|
|
classroom = Classroom(
|
|
# campus_id=form.campus_id.data,
|
|
building=form.building.data,
|
|
code=form.room_number.data,
|
|
floor=form.floor.data,
|
|
capacity=form.capacity.data,
|
|
description=form.description.data,
|
|
is_active=form.is_active.data
|
|
)
|
|
|
|
db.session.add(classroom)
|
|
db.session.commit()
|
|
|
|
flash(f'Classroom {classroom.code_display} has been created successfully!', 'success')
|
|
return redirect(url_for('classrooms.list_classrooms'))
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
flash(f'Error creating classroom: {str(e)}', 'error')
|
|
|
|
return render_template('classrooms/add.html', form=form, title='Add Classroom')
|
|
|
|
@classrooms_bp.route('/', endpoint='index')
|
|
@classrooms_bp.route('/list')
|
|
@login_required
|
|
def list_classrooms():
|
|
"""List all classrooms"""
|
|
try:
|
|
# Get filters
|
|
page = request.args.get('page', 1, type=int)
|
|
active_only = request.args.get('active_only', 'true').lower() == 'true'
|
|
search = request.args.get('search', '', type=str)
|
|
|
|
# Build query
|
|
query = Classroom.query
|
|
|
|
if active_only:
|
|
query = query.filter_by(is_active=True)
|
|
|
|
if search:
|
|
query = query.filter(
|
|
db.or_(
|
|
Classroom.building.ilike(f'%{search}%'),
|
|
Classroom.code.ilike(f'%{search}%'),
|
|
Classroom.description.ilike(f'%{search}%')
|
|
)
|
|
)
|
|
|
|
# Paginate results
|
|
classrooms = query.order_by(Classroom.building, Classroom.floor, Classroom.code).paginate(
|
|
page=page, per_page=12, error_out=False
|
|
)
|
|
|
|
return render_template('classrooms/list.html',
|
|
classrooms=classrooms,
|
|
active_only=active_only,
|
|
search=search,
|
|
title='Classrooms')
|
|
|
|
except Exception as e:
|
|
flash(f'Error loading classrooms: {str(e)}', 'error')
|
|
return render_template('classrooms/list.html', classrooms=None, title='Classrooms')
|
|
|
|
@classrooms_bp.route('/<int:id>')
|
|
@login_required
|
|
def view_classroom(id):
|
|
"""View classroom details"""
|
|
try:
|
|
classroom = Classroom.query.get_or_404(id)
|
|
|
|
# Get upcoming reservations for this classroom
|
|
from app.models.reservation import Reservation, ReservationStatus
|
|
|
|
upcoming_reservations = Reservation.query.filter(
|
|
Reservation.classroom_id == id,
|
|
Reservation.start_time > datetime.utcnow(),
|
|
Reservation.status == ReservationStatus.CONFIRMED.value
|
|
).order_by(Reservation.start_time).limit(10).all()
|
|
|
|
return render_template('classrooms/view.html',
|
|
classroom=classroom,
|
|
upcoming_reservations=upcoming_reservations,
|
|
title=f'Classroom {classroom.code_display}')
|
|
|
|
except Exception as e:
|
|
flash(f'Error loading classroom: {str(e)}', 'error')
|
|
return redirect(url_for('classrooms.list_classrooms'))
|
|
|
|
@classrooms_bp.route('/<int:id>/edit', methods=['GET', 'POST'])
|
|
@login_required
|
|
def edit_classroom(id):
|
|
"""Edit classroom"""
|
|
classroom = Classroom.query.get_or_404(id)
|
|
form = ClassroomForm(obj=classroom)
|
|
|
|
# TODO: Update when campus model is implemented
|
|
# form.campus_id.choices = [(1, 'Main Campus')]
|
|
|
|
if form.validate_on_submit():
|
|
try:
|
|
# Check for duplicates (excluding current classroom)
|
|
existing_classroom = Classroom.query.filter(
|
|
Classroom.building == form.building.data,
|
|
Classroom.code == form.room_number.data,
|
|
Classroom.floor == form.floor.data,
|
|
Classroom.id != id
|
|
).first()
|
|
|
|
if existing_classroom:
|
|
flash('Classroom with this building, room number, and floor already exists.', 'error')
|
|
return render_template('classrooms/edit.html', form=form, classroom=classroom, title='Edit Classroom')
|
|
|
|
# Update classroom
|
|
# classroom.campus_id = form.campus_id.data
|
|
classroom.building = form.building.data
|
|
classroom.code = form.room_number.data
|
|
classroom.floor = form.floor.data
|
|
classroom.capacity = form.capacity.data
|
|
classroom.description = form.description.data
|
|
classroom.is_active = form.is_active.data
|
|
classroom.updated_at = datetime.utcnow()
|
|
|
|
db.session.commit()
|
|
|
|
flash(f'Classroom {classroom.code_display} has been updated successfully!', 'success')
|
|
return redirect(url_for('classrooms.view_classroom', id=classroom.id))
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
flash(f'Error updating classroom: {str(e)}', 'error')
|
|
|
|
return render_template('classrooms/edit.html', form=form, classroom=classroom, title='Edit Classroom')
|
|
|
|
@classrooms_bp.route('/<int:id>/toggle', methods=['POST'])
|
|
@login_required
|
|
def toggle_classroom(id):
|
|
"""Toggle classroom active status"""
|
|
classroom = Classroom.query.get_or_404(id)
|
|
|
|
try:
|
|
classroom.is_active = not classroom.is_active
|
|
classroom.updated_at = datetime.utcnow()
|
|
db.session.commit()
|
|
|
|
status = 'activated' if classroom.is_active else 'deactivated'
|
|
flash(f'Classroom {classroom.code_display} has been {status} successfully!', 'success')
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
flash(f'Error updating classroom status: {str(e)}', 'error')
|
|
|
|
return redirect(request.referrer or url_for('classrooms.list_classrooms'))
|
|
|
|
# API endpoints for AJAX requests
|
|
@classrooms_bp.route('/api/available')
|
|
@login_required
|
|
def get_available_classrooms():
|
|
"""Get available classrooms for a specific time slot"""
|
|
start_time = request.args.get('start_time')
|
|
end_time = request.args.get('end_time')
|
|
|
|
if not start_time or not end_time:
|
|
return jsonify({'error': 'Start time and end time are required'}), 400
|
|
|
|
try:
|
|
from datetime import datetime
|
|
from app.models.reservation import Reservation, ReservationStatus
|
|
|
|
start_dt = datetime.fromisoformat(start_time)
|
|
end_dt = datetime.fromisoformat(end_time)
|
|
|
|
# Find all classrooms
|
|
all_classrooms = Classroom.query.filter_by(is_active=True).all()
|
|
available_classrooms = []
|
|
|
|
for classroom in all_classrooms:
|
|
# Check if classroom has conflicting reservations
|
|
conflicts = Reservation.query.filter(
|
|
Reservation.classroom_id == classroom.id,
|
|
Reservation.status == ReservationStatus.CONFIRMED.value,
|
|
Reservation.start_time < end_dt,
|
|
Reservation.end_time > start_dt
|
|
).all()
|
|
|
|
if not conflicts:
|
|
available_classrooms.append({
|
|
'id': classroom.id,
|
|
'code': classroom.code,
|
|
'building': classroom.building,
|
|
'room_number': classroom.code,
|
|
'floor': classroom.floor,
|
|
'capacity': classroom.capacity,
|
|
'code_display': classroom.code_display
|
|
})
|
|
|
|
return jsonify({'classrooms': available_classrooms})
|
|
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500 |