214 lines
7.7 KiB
Python
214 lines
7.7 KiB
Python
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
|
|
from flask_login import login_required
|
|
from app import db
|
|
from app.models.classroom import Classroom, ClassroomResource
|
|
from app.models.reservation import Reservation, ReservationStatus
|
|
from app.forms.classroom import ClassroomForm, ClassroomResourceForm
|
|
from datetime import datetime
|
|
|
|
classrooms_bp = Blueprint('classrooms', __name__, url_prefix='/classrooms')
|
|
|
|
@classrooms_bp.route('/')
|
|
@login_required
|
|
def list_classrooms():
|
|
"""List all classrooms"""
|
|
page = request.args.get('page', 1, type=int)
|
|
search = request.args.get('search', '')
|
|
campus = request.args.get('campus', '')
|
|
|
|
query = Classroom.query.filter_by(is_active=True)
|
|
|
|
if search:
|
|
query = query.filter(
|
|
Classroom.code.contains(search) |
|
|
Classroom.name.contains(search) |
|
|
Classroom.building.contains(search)
|
|
)
|
|
|
|
if campus:
|
|
query = query.filter(Classroom.campus_location == campus)
|
|
|
|
classrooms = query.paginate(
|
|
page=page,
|
|
per_page=20,
|
|
error_out=False
|
|
)
|
|
|
|
return render_template('classrooms/list.html',
|
|
classrooms=classrooms,
|
|
search=search,
|
|
campus=campus)
|
|
|
|
@classrooms_bp.route('/add', methods=['GET', 'POST'])
|
|
@login_required
|
|
def add_classroom():
|
|
"""Add new classroom"""
|
|
form = ClassroomForm()
|
|
|
|
if form.validate_on_submit():
|
|
classroom = Classroom(
|
|
code=form.code.data,
|
|
name=form.name.data,
|
|
building=form.building.data,
|
|
floor=form.floor.data,
|
|
capacity=form.capacity.data,
|
|
description=form.description.data,
|
|
campus_location=form.campus_location.data
|
|
)
|
|
|
|
try:
|
|
db.session.add(classroom)
|
|
db.session.commit()
|
|
flash(f'Classroom {classroom.code} added successfully!', 'success')
|
|
return redirect(url_for('classrooms.list_classrooms'))
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
flash('Error adding classroom. Classroom code might already exist.', 'error')
|
|
|
|
return render_template('classrooms/add.html', form=form)
|
|
|
|
@classrooms_bp.route('/edit/<int:Id>', methods=['GET', 'POST'])
|
|
@login_required
|
|
def edit_classroom(Id):
|
|
"""Edit classroom"""
|
|
classroom = Classroom.query.get_or_404(Id)
|
|
|
|
if not classroom.is_active:
|
|
flash('This classroom has been deactivated.', 'warning')
|
|
return redirect(url_for('classrooms.list_classrooms'))
|
|
|
|
form = ClassroomForm(obj=classroom)
|
|
|
|
if form.validate_on_submit():
|
|
try:
|
|
classroom.code = form.code.data
|
|
classroom.name = form.name.data
|
|
classroom.building = form.building.data
|
|
classroom.floor = form.floor.data
|
|
classroom.capacity = form.capacity.data
|
|
classroom.description = form.description.data
|
|
classroom.campus_location = form.campus_location.data
|
|
classroom.updated_at = datetime.utcnow()
|
|
|
|
db.session.commit()
|
|
flash(f'Classroom {classroom.code} updated successfully!', 'success')
|
|
return redirect(url_for('classrooms.view_classroom', Id=classroom.id))
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
flash('Error updating classroom.', 'error')
|
|
|
|
return render_template('classrooms/edit.html', form=form, classroom=classroom)
|
|
|
|
@classrooms_bp.route('/view/<int:Id>')
|
|
@login_required
|
|
def view_classroom(Id):
|
|
"""View classroom details"""
|
|
classroom = Classroom.query.get_or_404(Id)
|
|
|
|
if not classroom.is_active:
|
|
flash('This classroom has been deactivated.', 'warning')
|
|
return redirect(url_for('classrooms.list_classrooms'))
|
|
|
|
# Get classroom resources
|
|
resources = ClassroomResource.query.filter_by(classroom_id=Id).all()
|
|
|
|
# Get upcoming reservations
|
|
upcoming_reservations = Reservation.query.filter(
|
|
Reservation.classroom_id == Id,
|
|
Reservation.start_time >= datetime.utcnow(),
|
|
Reservation.status.in_([ReservationStatus.CONFIRMED, ReservationStatus.PENDING])
|
|
).order_by(Reservation.start_time).limit(10).all()
|
|
|
|
return render_template('classrooms/view.html',
|
|
classroom=classroom,
|
|
resources=resources,
|
|
upcoming_reservations=upcoming_reservations)
|
|
|
|
@classrooms_bp.route('/delete/<int:Id>')
|
|
@login_required
|
|
def delete_classroom(Id):
|
|
"""Delete/deactivate classroom"""
|
|
classroom = Classroom.query.get_or_404(Id)
|
|
|
|
# Check for active reservations
|
|
active_reservations = Reservation.query.filter(
|
|
Reservation.classroom_id == Id,
|
|
Reservation.status.in_([ReservationStatus.CONFIRMED, ReservationStatus.PENDING])
|
|
).count()
|
|
|
|
if active_reservations > 0:
|
|
flash('Cannot delete classroom with active reservations.', 'error')
|
|
return redirect(url_for('classrooms.view_classroom', Id=Id))
|
|
|
|
try:
|
|
classroom.is_active = False
|
|
classroom.updated_at = datetime.utcnow()
|
|
db.session.commit()
|
|
flash(f'Classroom {classroom.code} has been deactivated.', 'success')
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
flash('Error deactivating classroom.', 'error')
|
|
|
|
return redirect(url_for('classrooms.list_classrooms'))
|
|
|
|
@classrooms_bp.route('/<int:classroom_id>/add_resource', methods=['GET', 'POST'])
|
|
@login_required
|
|
def add_resource(classroom_id):
|
|
"""Add resource to classroom"""
|
|
classroom = Classroom.query.get_or_404(classroom_id)
|
|
form = ClassroomResourceForm()
|
|
|
|
if form.validate_on_submit():
|
|
resource = ClassroomResource(
|
|
classroom_id=classroom_id,
|
|
resource_type=form.resource_type.data,
|
|
brand=form.brand.data,
|
|
model=form.model.data,
|
|
specifications=form.specifications.data,
|
|
quantity=form.quantity.data
|
|
)
|
|
|
|
try:
|
|
db.session.add(resource)
|
|
db.session.commit()
|
|
flash(f'{resource.resource_type} added to {classroom.code} successfully!', 'success')
|
|
return redirect(url_for('classrooms.view_classroom', Id=classroom_id))
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
flash('Error adding resource.', 'error')
|
|
|
|
return render_template('classrooms/add_resource.html', form=form, classroom=classroom)
|
|
|
|
@classrooms_bp.route('/api/available')
|
|
@login_required
|
|
def api_available_classrooms():
|
|
"""API endpoint to get available classrooms"""
|
|
start_time = request.args.get('start_time')
|
|
end_time = request.args.get('end_time')
|
|
min_capacity = request.args.get('min_capacity', 0, type=int)
|
|
|
|
if not start_time or not end_time:
|
|
return jsonify({'error': 'start_time and end_time are required'}), 400
|
|
|
|
try:
|
|
start_dt = datetime.fromisoformat(start_time)
|
|
end_dt = datetime.fromisoformat(end_time)
|
|
except ValueError:
|
|
return jsonify({'error': 'Invalid datetime format'}), 400
|
|
|
|
# Find available classrooms (without conflicts)
|
|
conflicting_classroom_ids = [
|
|
r.classroom_id for r in Reservation.query.filter(
|
|
Reservation.start_time < end_dt,
|
|
Reservation.end_time > start_dt,
|
|
Reservation.status == ReservationStatus.CONFIRMED
|
|
).all()
|
|
]
|
|
|
|
available_classrooms = Classroom.query.filter(
|
|
Classroom.is_active == True,
|
|
Classroom.capacity >= min_capacity,
|
|
~Classroom.id.in_(conflicting_classroom_ids)
|
|
).all()
|
|
|
|
return jsonify([classroom.to_dict() for classroom in available_classrooms]) |