diff --git a/app/models/classroom.py b/app/models/classroom.py index 1c4938c..a184cb1 100644 --- a/app/models/classroom.py +++ b/app/models/classroom.py @@ -5,18 +5,27 @@ class Classroom(db.Model): __tablename__ = 'classrooms' id = db.Column(db.Integer, primary_key=True) - # campus_id = db.Column(db.Integer, db.ForeignKey('campuses.id'), nullable=False) + campus_id = db.Column(db.Integer, nullable=True) # Maps to campus_id in DB building = db.Column(db.String(100), nullable=False) - code = db.Column(db.String(50), nullable=False) # Changed from room_number - name = db.Column(db.String(100), nullable=False) + code = db.Column('room_number', db.String(50), nullable=False) # Maps to room_number column in DB floor = db.Column(db.Integer, nullable=False) capacity = db.Column(db.Integer, nullable=False) description = db.Column(db.Text) - campus_location = db.Column(db.String(100)) - is_active = db.Column(db.Boolean, default=True, nullable=False) + is_active = db.Column('active', db.Boolean, default=True, nullable=False) # Maps to active column in DB created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + # Virtual properties for compatibility + @property + def name(self): + """Generate name from building and code""" + return f"{self.building}-{self.code}" + + @property + def campus_location(self): + """Placeholder for campus location - can be expanded when campus model is implemented""" + return None + # Virtual field for compatibility - combines building and code @property def room_number(self): diff --git a/app/models/genetic_algorithm.py b/app/models/genetic_algorithm.py index f698600..c25147c 100644 --- a/app/models/genetic_algorithm.py +++ b/app/models/genetic_algorithm.py @@ -334,11 +334,11 @@ class ReservationOptimizer: 'commission_id': gene.commission_id, 'classroom_id': gene.classroom_id, 'user_id': admin_user_id, - 'start_time': gene.start_time, - 'end_time': gene.end_time, + 'start_time': gene.start_time.isoformat() if gene.start_time else None, + 'end_time': gene.end_time.isoformat() if gene.end_time else None, 'purpose': gene.purpose, 'expected_attendees': gene.expected_attendees, - 'status': ReservationStatus.PENDING + 'status': ReservationStatus.PENDING.value # Convert enum to string } optimized_reservations.append(reservation_data) diff --git a/app/models/reservation.py b/app/models/reservation.py index 06b31ef..8974871 100644 --- a/app/models/reservation.py +++ b/app/models/reservation.py @@ -15,18 +15,27 @@ class Reservation(db.Model): id = db.Column(db.Integer, primary_key=True) classroom_id = db.Column(db.Integer, db.ForeignKey('classrooms.id'), nullable=False) commission_id = db.Column(db.Integer, db.ForeignKey('commissions.id'), nullable=False) - user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) + user_id = db.Column('teacher_id', db.Integer, db.ForeignKey('users.id'), nullable=False) # Maps to teacher_id in DB start_time = db.Column(db.DateTime, nullable=False) end_time = db.Column(db.DateTime, nullable=False) purpose = db.Column(db.String(500), nullable=False) - status = db.Column(db.Enum(ReservationStatus), default=ReservationStatus.PENDING, nullable=False) + status = db.Column(db.String(20), default='PENDING', nullable=False) # DB uses VARCHAR not ENUM expected_attendees = db.Column(db.Integer, nullable=False) notes = db.Column(db.Text) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + # Virtual property to convert status string to enum + @property + def status_enum(self): + """Get status as enum""" + try: + return ReservationStatus(self.status) + except (ValueError, AttributeError): + return ReservationStatus.PENDING + # Relationships classroom = db.relationship('Classroom', backref=db.backref('reservation_list', lazy=True, cascade='all, delete-orphan')) commission = db.relationship('Commission', backref=db.backref('reservation_list', lazy=True, cascade='all, delete-orphan')) @@ -42,7 +51,7 @@ class Reservation(db.Model): 'start_time': self.start_time.isoformat() if self.start_time else None, 'end_time': self.end_time.isoformat() if self.end_time else None, 'purpose': self.purpose, - 'status': self.status.value if self.status else None, + 'status': self.status, # Already a string in DB 'expected_attendees': self.expected_attendees, 'notes': self.notes, 'created_at': self.created_at.isoformat() if self.created_at else None, @@ -54,7 +63,7 @@ class Reservation(db.Model): def is_confirmed(self): """Check if reservation is confirmed""" - return self.status == ReservationStatus.CONFIRMED + return self.status == ReservationStatus.CONFIRMED.value def can_be_cancelled(self): """Check if reservation can be cancelled (24 hours before start)""" @@ -65,9 +74,9 @@ class Reservation(db.Model): """Check if this reservation conflicts with another""" if self.classroom_id != other_reservation.classroom_id: return False - if self.status != ReservationStatus.CONFIRMED: + if self.status != ReservationStatus.CONFIRMED.value: return False - if other_reservation.status != ReservationStatus.CONFIRMED: + if other_reservation.status != ReservationStatus.CONFIRMED.value: return False # Check time overlap @@ -79,7 +88,7 @@ class Reservation(db.Model): """Find conflicting reservations for a specific classroom and time slot""" query = Reservation.query.filter( Reservation.classroom_id == classroom_id, - Reservation.status == ReservationStatus.CONFIRMED, + Reservation.status == ReservationStatus.CONFIRMED.value, Reservation.start_time < end_time, Reservation.end_time > start_time ) diff --git a/app/models/subject.py b/app/models/subject.py index d3b3257..065e23a 100644 --- a/app/models/subject.py +++ b/app/models/subject.py @@ -11,7 +11,7 @@ class Subject(db.Model): department = db.Column(db.String(100), nullable=False) credits = db.Column(db.Integer, nullable=False) # duration_in_minutes = db.Column(db.Integer, nullable=False) - is_active = db.Column(db.Boolean, default=True, nullable=False) + is_active = db.Column('active', db.Boolean, default=True, nullable=False) # Maps to active column in DB created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) diff --git a/app/routes/classrooms.py b/app/routes/classrooms.py index f239a81..092f566 100644 --- a/app/routes/classrooms.py +++ b/app/routes/classrooms.py @@ -34,7 +34,6 @@ def add_classroom(): # campus_id=form.campus_id.data, building=form.building.data, code=form.room_number.data, - name=f"{form.building.data}-{form.room_number.data}", floor=form.floor.data, capacity=form.capacity.data, description=form.description.data, @@ -106,7 +105,7 @@ def view_classroom(id): upcoming_reservations = Reservation.query.filter( Reservation.classroom_id == id, Reservation.start_time > datetime.utcnow(), - Reservation.status == ReservationStatus.CONFIRMED + Reservation.status == ReservationStatus.CONFIRMED.value ).order_by(Reservation.start_time).limit(10).all() return render_template('classrooms/view.html', @@ -146,7 +145,6 @@ def edit_classroom(id): # classroom.campus_id = form.campus_id.data classroom.building = form.building.data classroom.code = form.room_number.data - classroom.name = f"{form.building.data}-{form.room_number.data}" classroom.floor = form.floor.data classroom.capacity = form.capacity.data classroom.description = form.description.data @@ -210,7 +208,7 @@ def get_available_classrooms(): # Check if classroom has conflicting reservations conflicts = Reservation.query.filter( Reservation.classroom_id == classroom.id, - Reservation.status == ReservationStatus.CONFIRMED, + Reservation.status == ReservationStatus.CONFIRMED.value, Reservation.start_time < end_dt, Reservation.end_time > start_dt ).all() diff --git a/app/routes/genetic_algorithm.py b/app/routes/genetic_algorithm.py index 1b0faf7..cfa1848 100644 --- a/app/routes/genetic_algorithm.py +++ b/app/routes/genetic_algorithm.py @@ -210,7 +210,7 @@ def get_algorithm_status(): total_classrooms = Classroom.query.filter_by(is_active=True).count() total_commissions = Commission.query.filter_by(active=True).count() - total_reservations = Reservation.query.filter_by(status=ReservationStatus.CONFIRMED).count() + total_reservations = Reservation.query.filter_by(status=ReservationStatus.CONFIRMED.value).count() return jsonify({ 'success': True, diff --git a/app/routes/schedule.py b/app/routes/schedule.py index 20bbd77..f92976c 100644 --- a/app/routes/schedule.py +++ b/app/routes/schedule.py @@ -27,7 +27,7 @@ def today_schedule(): ).filter( Reservation.start_time >= today_start, Reservation.start_time <= today_end, - Reservation.status.in_([ReservationStatus.CONFIRMED, ReservationStatus.PENDING]) + Reservation.status.in_([ReservationStatus.CONFIRMED.value, ReservationStatus.PENDING.value]) ).order_by(Reservation.start_time).all() # Create time blocks for timeline view @@ -49,7 +49,7 @@ def today_schedule(): # Calculate stats total_reservations = len(reservations) - confirmed = len([r for r in reservations if r.status == ReservationStatus.CONFIRMED]) + confirmed = len([r for r in reservations if r.status == ReservationStatus.CONFIRMED.value]) pending = len([r for r in reservations if r.status == ReservationStatus.PENDING]) # Get total classrooms and calculate occupied ones @@ -158,7 +158,7 @@ def add_reservation(): from app.models.reservation import ReservationStatus existing_reservation = Reservation.query.filter_by( classroom_id=form.classroom_id.data, - status=ReservationStatus.CONFIRMED + status=ReservationStatus.CONFIRMED.value ).filter( Reservation.start_time < form.end_time.data, Reservation.end_time > form.start_time.data @@ -177,7 +177,7 @@ def add_reservation(): end_time=form.end_time.data, purpose=form.purpose.data, notes=form.notes.data, - status=ReservationStatus.PENDING, + status=ReservationStatus.PENDING.value, expected_attendees=form.expected_attendees.data ) @@ -209,11 +209,11 @@ def confirm_reservation(id): reservation = Reservation.query.get_or_404(id) # Validación de seguridad: Solo confirmar si está pendiente - if reservation.status != ReservationStatus.PENDING: + if reservation.status != ReservationStatus.PENDING.value: flash('Only pending reservations can be confirmed.', 'warning') return redirect(url_for('schedule.today_schedule')) - reservation.status = ReservationStatus.CONFIRMED + reservation.status = ReservationStatus.CONFIRMED.value db.session.commit() flash( @@ -343,7 +343,7 @@ def calendar_data(): ).filter( Reservation.start_time >= start_date, Reservation.start_time <= end_date, - Reservation.status.in_([ReservationStatus.CONFIRMED, ReservationStatus.PENDING]) + Reservation.status.in_([ReservationStatus.CONFIRMED.value, ReservationStatus.PENDING.value]) ).order_by(Reservation.start_time).all() events = [] @@ -384,7 +384,7 @@ def api_today_events(): ).filter( Reservation.start_time >= today_start, Reservation.start_time <= today_end, - Reservation.status == ReservationStatus.CONFIRMED + Reservation.status == ReservationStatus.CONFIRMED.value ).order_by(Reservation.start_time).all() events = []