import os from datetime import datetime from flask import Flask, request, jsonify, abort from config import Config from models import db, Car, Reservation, CarType from flask_migrate import Migrate app = Flask(__name__) app.config.from_object(Config) db.init_app(app) # Migrations Migrate(app, db) @app.errorhandler(404) def not_found(e): return jsonify({'error': 'Not found'}), 404 @app.errorhandler(400) def bad_request(e): return jsonify({'error': 'Bad request', 'message': str(e)}), 400 @app.route('/health', methods=['GET']) def health(): return jsonify({'status': 'ok'}) @app.route('/cars', methods=['GET']) def list_cars(): cars = Car.query.all() return jsonify([c.to_dict() for c in cars]) @app.route('/cars/', methods=['GET']) def get_car(car_id): car = Car.query.get_or_404(car_id) return jsonify(car.to_dict()) @app.route('/cars', methods=['POST']) def create_car(): data = request.get_json() or {} required = ['name', 'seats', 'ports', 'type', 'brand', 'license_plate'] for f in required: if f not in data: abort(400, f"Missing field: {f}") # Validate ports if data['ports'] not in (3, 5): abort(400, 'ports must be either 3 or 5') # Validate type try: car_type = CarType(data['type']) except ValueError: abort(400, "type must be one of 'suv', 'small', 'van'") # Create car = Car( name=data['name'], seats=int(data['seats']), ports=int(data['ports']), type=car_type, brand=data['brand'], license_plate=data['license_plate'], ) db.session.add(car) try: db.session.commit() except Exception as e: db.session.rollback() abort(400, f'Could not create car: {e}') return jsonify(car.to_dict()), 201 @app.route('/cars/', methods=['PUT']) def update_car(car_id): car = Car.query.get_or_404(car_id) data = request.get_json() or {} if 'name' in data: car.name = data['name'] if 'seats' in data: car.seats = int(data['seats']) if 'ports' in data: if data['ports'] not in (3, 5): abort(400, 'ports must be either 3 or 5') car.ports = int(data['ports']) if 'type' in data: try: car.type = CarType(data['type']) except ValueError: abort(400, "type must be one of 'suv', 'small', 'van'") if 'brand' in data: car.brand = data['brand'] if 'license_plate' in data: car.license_plate = data['license_plate'] try: db.session.commit() except Exception as e: db.session.rollback() abort(400, f'Could not update car: {e}') return jsonify(car.to_dict()) @app.route('/cars/', methods=['DELETE']) def delete_car(car_id): car = Car.query.get_or_404(car_id) db.session.delete(car) db.session.commit() return jsonify({'deleted': car_id}) # Reservations @app.route('/reservations', methods=['GET']) def list_reservations(): res = Reservation.query.all() return jsonify([r.to_dict() for r in res]) @app.route('/reservations/', methods=['GET']) def get_reservation(res_id): r = Reservation.query.get_or_404(res_id) return jsonify(r.to_dict()) @app.route('/reservations', methods=['POST']) def create_reservation(): data = request.get_json() or {} required = ['pickup_date', 'return_date', 'car_id', 'customer_fullname', 'drive_license'] for f in required: if f not in data: abort(400, f"Missing field: {f}") try: pickup = datetime.fromisoformat(data['pickup_date']) ret = datetime.fromisoformat(data['return_date']) except Exception: abort(400, 'pickup_date and return_date must be ISO datetime strings') if ret <= pickup: abort(400, 'return_date must be after pickup_date') car = Car.query.get(data['car_id']) if not car: abort(400, 'car_id does not exist') reservation = Reservation( pickup_date=pickup, return_date=ret, car_id=int(data['car_id']), customer_fullname=data['customer_fullname'], drive_license=data['drive_license'], ) db.session.add(reservation) try: db.session.commit() except Exception as e: db.session.rollback() abort(400, f'Could not create reservation: {e}') return jsonify(reservation.to_dict()), 201 @app.route('/reservations/', methods=['PUT']) def update_reservation(res_id): r = Reservation.query.get_or_404(res_id) data = request.get_json() or {} if 'pickup_date' in data: try: r.pickup_date = datetime.fromisoformat(data['pickup_date']) except Exception: abort(400, 'pickup_date must be ISO datetime string') if 'return_date' in data: try: r.return_date = datetime.fromisoformat(data['return_date']) except Exception: abort(400, 'return_date must be ISO datetime string') if 'car_id' in data: car = Car.query.get(data['car_id']) if not car: abort(400, 'car_id does not exist') r.car_id = int(data['car_id']) if 'customer_fullname' in data: r.customer_fullname = data['customer_fullname'] if 'drive_license' in data: r.drive_license = data['drive_license'] if r.return_date <= r.pickup_date: abort(400, 'return_date must be after pickup_date') try: db.session.commit() except Exception as e: db.session.rollback() abort(400, f'Could not update reservation: {e}') return jsonify(r.to_dict()) @app.route('/reservations/', methods=['DELETE']) def delete_reservation(res_id): r = Reservation.query.get_or_404(res_id) db.session.delete(r) db.session.commit() return jsonify({'deleted': res_id})