diff --git a/app/api/routes.py b/app/api/routes.py index 2503793f50f195a0bf7e54f7d1e2a234dcdf4731..5a89cef38ddd46476b650723b5b36f9d04084741 100644 --- a/app/api/routes.py +++ b/app/api/routes.py @@ -96,24 +96,24 @@ data = [ def get_data(): query = db.session.query(Listings) - # Retrieve filter parameters from the request depart_location = request.args.get('depart_location') destination_location = request.args.get('destination_location') - min_depart_time = request.args.get('min_depart_time') - max_depart_time = request.args.get('max_depart_time') + depart_before_time = request.args.get('depart_before_time') + arrive_after_time = request.args.get('arrive_after_time') min_fair_cost = request.args.get('min_fair_cost') max_fair_cost = request.args.get('max_fair_cost') transport_type = request.args.get('transport_type') - # Apply filters dynamically if depart_location: - query = query.filter(Listings.depart_location == depart_location) + depart_locations = depart_location.split(',') + query = query.filter(Listings.depart_location.in_(depart_locations)) if destination_location: - query = query.filter(Listings.destination_location == destination_location) - if min_depart_time: - query = query.filter(Listings.depart_time >= min_depart_time) - if max_depart_time: - query = query.filter(Listings.depart_time <= max_depart_time) + destination_locations = destination_location.split(',') + query = query.filter(Listings.destination_location.in_(destination_locations)) + if depart_before_time: + query = query.filter(Listings.depart_time <= depart_before_time) + if arrive_after_time: + query = query.filter(Listings.destination_time >= arrive_after_time) if min_fair_cost: query = query.filter(Listings.fair_cost >= min_fair_cost) if max_fair_cost: @@ -121,16 +121,13 @@ def get_data(): if transport_type: query = query.filter(Listings.transport_type == transport_type) - # Fetch the filtered results in a single query filtered_data = query.all() - - # Convert the results to a list of dictionaries result = [ { 'depart_location': listing.depart_location, - 'depart_time': listing.depart_time, + 'depart_time': listing.depart_time.strftime("%H:%M"), 'destination_location': listing.destination_location, - 'destination_time': listing.destination_time, + 'destination_time': listing.destination_time.strftime("%H:%M"), 'fair_cost': listing.fair_cost, 'transport_type': listing.transport_type } for listing in filtered_data diff --git a/app/bookings/routes.py b/app/bookings/routes.py index 1618a934424e300cedc09962d04cff077966cdf7..c4f664e97c1ea1e55965e920717cbba959c57f72 100644 --- a/app/bookings/routes.py +++ b/app/bookings/routes.py @@ -3,17 +3,6 @@ from app.bookings import bp from app.models import Listings, ListingImages import json -@bp.route('/home') -def index(): - listing_ids = [] - top_listings = Listings.get_top_listings(5) - - for listing in top_listings: - listing_ids.append(listing.id) - - top_listing_images = ListingImages.get_selected_main_images(listing_ids) - - return render_template('bookings/index.html', top_listings=top_listings, top_listing_images=top_listing_images) @bp.route('/') def redirect_index(): @@ -32,7 +21,7 @@ def listings(): item.main_image_url = url_for('main.upload_file', filename=item.listing_images[0].image_location) else: item.main_image_url = "/path/to/default-image.jpg" - # Must replace with single quotes otherwise JS does not load modal correctly + # Must be a single quote JSON otherwise doesn't work in frontend item.image_urls = json.dumps([url_for('main.upload_file', filename=img.image_location) for img in item.listing_images]).replace('"', '"') return render_template('bookings/listings.html', items=all_listings) diff --git a/app/main/routes.py b/app/main/routes.py index 4932a5cbd9075210fa8b642d1a716030b2807d45..1970ff647df87d0007565b1426762db398c7642a 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -1,4 +1,5 @@ from flask import render_template, send_from_directory +from app.models import Listings, ListingImages from app.main import bp import datetime import os @@ -6,8 +7,21 @@ import os @bp.route('/') def index(): date=datetime.datetime.now() + listing_ids = [] tomorrow_object = date + datetime.timedelta(days=1) - return render_template('index.html', today = date.strftime('%Y-%m-%d'), tomorrow = tomorrow_object.strftime('%Y-%m-%d')) + top_listings = Listings.get_top_listings(5) + + for listing in top_listings: + listing_ids.append(listing.id) + + top_listing_images = ListingImages.get_selected_main_images(listing_ids) + return render_template( + 'index.html', + today = date.strftime('%Y-%m-%d'), + tomorrow = tomorrow_object.strftime('%Y-%m-%d'), + top_listings=top_listings, + top_listing_images=top_listing_images + ) @bp.route('/uploads/listing_images/<filename>') def upload_file(filename): diff --git a/app/models/listings.py b/app/models/listings.py index ee402f0c8319154e1969c7057ed29546832726c7..7295ec438b51d223fa06937ab7cf5fd59201dc75 100644 --- a/app/models/listings.py +++ b/app/models/listings.py @@ -1,5 +1,5 @@ from sqlalchemy.orm import relationship -from sqlalchemy import Integer, ForeignKey +from sqlalchemy import Time from app import db class Listings(db.Model): @@ -7,10 +7,10 @@ class Listings(db.Model): id = db.Column(db.Integer(), nullable=False, primary_key=True) depart_location = db.Column(db.String(255), nullable=False) - depart_time = db.Column(db.DateTime(), nullable=False) + depart_time = db.Column(Time, nullable=False) destination_location = db.Column(db.String(255), nullable=False) - destination_time = db.Column(db.DateTime(), nullable=False) - fair_cost = db.Column(db.Float(), nullable=False) # Removed (2) from Float, it's not required + destination_time = db.Column(Time, nullable=False) + fair_cost = db.Column(db.Float(), nullable=False) transport_type = db.Column(db.String(255), nullable=False) listing_images = relationship("ListingImages", back_populates="listing", cascade="all, delete-orphan") @@ -34,10 +34,3 @@ class Listings(db.Model): @classmethod def get_top_listings(cls, amount_of_listings=5): return cls.query.limit(amount_of_listings).all() - - -# .order_by( -# cls.economy_tickets, -# cls.business_tickets -# ). - \ No newline at end of file diff --git a/app/templates/admin/manage_bookings.html b/app/templates/admin/manage_bookings.html index c5feb80281f69a0b67bd18abbaf0db02d4e7bca6..2beece98d137def348371c142905a0ab425edd5d 100644 --- a/app/templates/admin/manage_bookings.html +++ b/app/templates/admin/manage_bookings.html @@ -1,5 +1,45 @@ {% extends 'base.html' %} {% block content %} +<style> + .dataTables_wrapper .dataTables_paginate { + margin-top: 20px; + } + + .dataTables_wrapper .dataTables_paginate .paginate_button { + padding: 5px 10px; + margin-left: 5px; + color: white !important; + background-color: #0D6EFD; + border-radius: 5px; + border: none !important; + } + + .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover, + .dataTables_wrapper .dataTables_paginate .paginate_button.current { + background: #ffa500 !important; + border: none !important; + color: white !important; + } + + .dataTables_wrapper .dataTables_paginate .paginate_button:hover, + .dataTables_wrapper .dataTables_paginate .paginate_button:focus { + background: #87CEEB !important; + color: #244855 !important; + } + + .dataTables_wrapper .dataTables_paginate .paginate_button.disabled, + .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover { + background-color: #B0C4DE !important; + color: white !important; + } + + .dataTables_wrapper .dataTables_info { + margin-top: 10px; + font-size: 14px; + color: white; + } +</style> + <div class="container my-4"> <div class="d-flex justify-content-between mb-3"> <h2>Manage Bookings</h2> @@ -20,23 +60,25 @@ <div class="mb-3"> <label for="depart_location" class="form-label">Depart Location:</label> <select class="form-control select2-multiple" id="depart_location" name="depart_location[]" multiple="multiple"> - <!-- Options will be dynamically added --> </select> </div> <div class="mb-3"> <label for="destination_location" class="form-label">Destination Location:</label> <select class="form-control select2-multiple" id="destination_location" name="destination_location[]" multiple="multiple"> - <!-- Options will be dynamically added --> </select> </div> <div class="mb-3"> - <label for="min_depart_time" class="form-label">Minimum Depart Time:</label> - <input type="text" class="form-control" id="min_depart_time" name="min_depart_time"> + <label for="depart_before_time" class="form-label">Depart Before:</label> + <select class="form-control select2-dropdown" id="depart_before_time" name="depart_before_time"> + <option value="" disabled selected>Select a time</option> + </select> </div> <div class="mb-3"> - <label for="max_depart_time" class="form-label">Maximum Depart Time:</label> - <input type="text" class="form-control" id="max_depart_time" name="max_depart_time"> - </div> + <label for="arrive_after_time" class="form-label">Arrive After:</label> + <select class="form-control select2-dropdown" id="arrive_after_time" name="arrive_after_time"> + <option value="" disabled selected>Select a time</option> + </select> + </div> <div class="mb-3"> <label for="min_fair_cost" class="form-label">Minimum Fair Cost:</label> <input type="number" class="form-control" id="min_fair_cost" name="min_fair_cost"> @@ -47,8 +89,8 @@ </div> <div class="mb-3"> <label for="transport_type" class="form-label">Transport Type:</label> - <input type="text" class="form-control" id="transport_type" name="transport_type"> - </div> + <input type="text" class="form-control" id="transport_type" name="transport_type" value="Airplane" disabled> + </div> </form> </div> <div class="modal-footer"> @@ -58,15 +100,16 @@ </div> </div> </div> - <!-- Data table starts --> + + <!-- Manage bookings table starts --> <div class="table-container"> - <table id="manage_bookings" class="table table-striped table-bordered display" style="width:100%"> + <table id="manage_bookings" class="table table-striped table-bordered display hover" style="width:100%"> <thead> <tr> <th>Depart Location</th> <th>Depart Time</th> <th>Destination Location</th> - <th>Arrival Time</th> + <th>Destination Time</th> <th>Fair Cost ($)</th> <th>Transport Type</th> <th>Actions</th> @@ -80,41 +123,81 @@ <style> .table-container { width: 100%; - overflow-x: auto; /* Allow horizontal scroll if table is too wide */ + overflow-x: auto; + overflow-y: hidden; } + @media (max-width: 800px) { .table-container { padding: 0 10px; - overflow-x: auto; } } + .dataTables_wrapper { width: 100%; } + + table.dataTable.no-footer { + margin-bottom: 30px; + } + + .select2-container .select2-selection--single { + height: 38px; + display: flex; + align-items: center; + text-align: left; + } + </style> <script> $(document).ready(function() { - // Initialize Select2 $('.select2-multiple').select2({ placeholder: "Select locations", width: '100%' }); + $('.select2-dropdown').select2({ + placeholder: "Select a time", + width: '100%', + minimumResultsForSearch: Infinity + }); + + const timeOptions = [ + "00:00", "00:30", "01:00", "01:30", "02:00", "02:30", "03:00", "03:30", "04:00", "04:30", + "05:00", "05:30", "06:00", "06:30", "07:00", "07:30", "08:00", "08:30", "09:00", "09:30", + "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", + "15:00", "15:30", "16:00", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00", "19:30", + "20:00", "20:30", "21:00", "21:30", "22:00", "22:30", "23:00", "23:30" + ]; + + timeOptions.forEach(time => { + $('#depart_before_time').append(new Option(time, time)); + $('#arrive_after_time').append(new Option(time, time)); + }); + + const locations = ['USA', 'Budapest', 'Location3'].sort(); + locations.forEach(location => { + $('#depart_location').append(new Option(location, location)); + $('#destination_location').append(new Option(location, location)); + }); + + // Load data table const table = $('#manage_bookings').DataTable({ - pageLength: 25, + pageLength: 10, lengthChange: false, searching: false, + ordering: false, ajax: { url: "{{ url_for('api.get_data') }}", dataSrc: '', data: function(d) { d.depart_location = $('#depart_location').val() ? $('#depart_location').val().join(',') : ''; d.destination_location = $('#destination_location').val() ? $('#destination_location').val().join(',') : ''; - d.min_depart_time = $('#min_depart_time').val(); - d.max_depart_time = $('#max_depart_time').val(); + d.depart_before_time = $('#depart_before_time').val(); + d.arrive_after_time = $('#arrive_after_time').val(); d.min_fair_cost = $('#min_fair_cost').val(); d.max_fair_cost = $('#max_fair_cost').val(); - d.transport_type = $('#transport_type').val(); + d.transport_type = 'Airplane'; //Hardcoded cause not offering other transport types currently } }, columns: [ @@ -138,13 +221,10 @@ </div> </div>` } - ] - }); - - // Dynamically populate the dropdowns with location options - const locations = ['USA', 'Budapest', 'Location3']; // Replace with actual locations - locations.forEach(location => { - $('#depart_location, #destination_location').append(new Option(location, location)); + ], + language: { + emptyTable: "No bookings could be found with the current filters" + } }); // Apply filters and reload table @@ -164,5 +244,9 @@ alert('Delete entry for ' + data.depart_location); }); }); + + </script> + + {% endblock %} diff --git a/app/templates/base.html b/app/templates/base.html index 417dfda5002325010913e8fe450d6eef0d4941bf..c628d89b82d6c2590bb528b5938c6db40118ee4d 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -1,10 +1,11 @@ <!DOCTYPE html> <html lang="en"> <head> + <!-- Existing dependencies --> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="csrf-token" content="{{ csrf_token() }}"> - <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='base.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='bootstrap_overrides.css') }}"> <link rel="stylesheet" href="https://kit.fontawesome.com/11fd621de6.js" crossorigin="anonymous"> @@ -12,17 +13,17 @@ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ttskch/select2-bootstrap4-theme@1.5.2/dist/select2-bootstrap4.min.css"> <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-beta.1/dist/css/select2.min.css" rel="stylesheet" /> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.39.0/css/tempusdominus-bootstrap-4.min.css" /> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <script src="https://unpkg.com/@popperjs/core@2/dist/umd/popper.js"></script> - <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.min.js"></script> - <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-beta.1/dist/css/select2.min.css" rel="stylesheet" /> <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-beta.1/dist/js/select2.min.js"></script> - + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/timepicker@1.13.18/jquery.timepicker.min.css"> + <script src="https://cdn.jsdelivr.net/npm/timepicker@1.13.18/jquery.timepicker.min.js"></script> <title>Horizon Travels</title> </head> - -<body> + <body> <div class="main_content"> <div class="wrapper"> {% block navigation %} @@ -39,7 +40,7 @@ <a class="nav-link mx-2 active" aria-current="page" href="{{ url_for('main.index') }}">Home</a> </li> <li class="nav-item"> - <a class="nav-link mx-2" href="{{ url_for('bookings.index') }}">Find a booking</a> + <a class="nav-link mx-2" href="{{ url_for('bookings.listings') }}">Find a booking</a> </li> <li class="nav-item dropdown"> <a class="nav-link mx-2 dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false"> diff --git a/app/templates/index.html b/app/templates/index.html index 1a2dcbafd11fe1648f6406439d71f205a737245b..4846c8f973e93274cf1706d3402ed669ce6e4c8b 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,12 +1,151 @@ {% extends 'base.html' %} {% block content %} - <div class="date_selector"> - <h2>Date selector / Searching</h2> - <input type="date" id="start" name="trip-start" value="{{today}}" min="{{today}}" max="2018-12-31" /> - <input type="date" id="start" name="trip-end" value="{{tomorrow}}" min="{{tomorrow}}" max="2099-12-31" /> +<head> + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='listings.css')}}"> + <script src="https://cdn.jsdelivr.net/npm/swiffy-slider@1.6.0/dist/js/swiffy-slider.min.js" crossorigin="anonymous" defer></script> + <link href="https://cdn.jsdelivr.net/npm/swiffy-slider@1.6.0/dist/css/swiffy-slider.min.css" rel="stylesheet" crossorigin="anonymous"> + <style> + .slider-wrapper { + transform: scale(0.8); + width: 100%; + } + </style> +</head> +<div class="container mt-4"> + <form id="travelForm" class="row g-3"> + <div class="col-md-6"> + <label for="departLocation" class="form-label">Departure Location:</label> + <input type="text" class="form-control" id="departLocation" name="departLocation" required> + </div> + <div class="col-md-6"> + <label for="departBeforeDate" class="form-label">Depart Before Date:</label> + <input type="date" class="form-control" id="departBeforeDate" name="departBeforeDate" required> + </div> + <div class="col-md-6"> + <label for="departBeforeTime" class="form-label">Depart Before Time:</label> + <select class="form-control select2-dropdown" id="departBeforeTime" name="departBeforeTime" required> + <option value="" disabled selected>Select a time</option> + </select> + </div> + <div class="col-md-6"> + <label for="arriveLocation" class="form-label">Arrival Location:</label> + <input type="text" class="form-control" id="arriveLocation" name="arriveLocation" required> + </div> + <div class="col-md-6"> + <label for="arriveAfterDate" class="form-label">Arrive After Date:</label> + <input type="date" class="form-control" id="arriveAfterDate" name="arriveAfterDate" required> + </div> + <div class="col-md-6"> + <label for="numSeats" class="form-label">Number of Seats:</label> + <input type="number" class="form-control" id="numSeats" name="numSeats" min="1" required> + </div> + <div class="col-md-6"> + <label for="seatType" class="form-label">Seat Type:</label> + <select id="seatType" class="form-select" name="seatType"> + <option value="economy">Economy</option> + <option value="business">Business</option> + <option value="firstClass">First Class</option> + </select> + </div> + <div class="col-md-6"> + <label for="tripType" class="form-label">Trip Type:</label> + <select id="tripType" class="form-select" name="tripType" onchange="toggleReturnFields()"> + <option value="oneWay">One-way</option> + <option value="return">Return</option> + </select> + </div> + <div class="col-md-6"> + <label for="returnDate" class="form-label">Return Date:</label> + <input type="date" class="form-control" id="returnDate" name="returnDate" disabled> + </div> + <div class="col-md-6"> + <label for="returnTime" class="form-label">Return Time:</label> + <select class="form-control select2-dropdown" id="returnTime" name="returnTime" disabled> + <option value="" disabled selected>Select a time</option> + </select> + </div> + <div class="col-12"> + <button type="submit" class="btn btn-primary">Search</button> + </div> + </form> + + <script> + $(document).ready(function() { + $('.select2-dropdown').select2({ + placeholder: "Select a time", + width: '100%', + minimumResultsForSearch: Infinity + }); + + const timeOptions = [ + "00:00", "00:30", "01:00", "01:30", "02:00", "02:30", "03:00", "03:30", "04:00", "04:30", + "05:00", "05:30", "06:00", "06:30", "07:00", "07:30", "08:00", "08:30", "09:00", "09:30", + "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", + "15:00", "15:30", "16:00", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00", "19:30", + "20:00", "20:30", "21:00", "21:30", "22:00", "22:30", "23:00", "23:30" + ]; + + timeOptions.forEach(time => { + $('#departBeforeTime').append(new Option(time, time)); + $('#arriveAfterTime').append(new Option(time, time)); + $('#returnTime').append(new Option(time, time)); + }); + }); + + function toggleReturnFields() { + var tripType = document.getElementById("tripType").value; + var returnDate = document.getElementById("returnDate"); + var returnTime = document.getElementById("returnTime"); + if (tripType === "return") { + returnDate.disabled = false; + returnTime.disabled = false; + } else { + returnDate.disabled = true; + returnTime.disabled = true; + } + } + </script> + </div> + + <div> + <div class="deals_text"><span class="deals_underline">Currently Hot Locations</span></div> + <div class="slider-wrapper"> + <div class="swiffy-slider slider-item-show2 slider-item-reveal slider-nav-outside slider-nav-round slider-nav-visible slider-indicators-outside slider-indicators-round slider-indicators-dark slider-nav-animation slider-nav-animation-fadein slider-item-first-visible"> + <ul class="slider-container py-4"> + {% if get_flashed_messages() %} + <div class="alert alert-danger" role="alert"> + get_flashed_messages() + </div> + {% endif %} + {% for listing in top_listings %} + <li class="slide-visible"> + <div class="card shadow h-100"> + <div class="ratio ratio-16x9"> + <th>{{top_listing_images[listing.id]}}</th> + <img src="{{ url_for('main.upload_file', filename=top_listing_images[listing.id]) }}" class="card-img-top" loading="lazy" alt="Main Image"> + </div> + <div class="card-body p-3 p-xl-4"> + <h3 class="card-title h5">{{listing.destination_location}}</h3> + <p class="card-text">Add Location description here once implemented</p> + <div><a href="#" class="btn btn-primary">Book now</a> + </div> + </div> + </div> + </li> + {% endfor %} + </ul> + + <button type="button" class="slider-nav" aria-label="Go left"></button> + <button type="button" class="slider-nav slider-nav-next" aria-label="Go right"></button> + + <div class="slider-indicators"> + <button class="active" aria-label="Go to slide"></button> + <button aria-label="Go to slide" class=""></button> + <button aria-label="Go to slide" class=""></button> + <button aria-label="Go to slide" class=""></button> + </div> + </div> </div> - <div> - <h3>Currently Hot Deals!</h3> - </div> -{% endblock %} \ No newline at end of file +</div> +{% endblock %} diff --git a/migrations/versions/ce28a5ddecee_change_depart_time_and_destination_time_.py b/migrations/versions/ce28a5ddecee_change_depart_time_and_destination_time_.py new file mode 100644 index 0000000000000000000000000000000000000000..d8cb78142f271d8730323806f86c3c570c5f1ce5 --- /dev/null +++ b/migrations/versions/ce28a5ddecee_change_depart_time_and_destination_time_.py @@ -0,0 +1,31 @@ +"""Change depart_time and destination_time from DateTime to Time + +Revision ID: ce28a5ddecee +Revises: 28932a02e785 +Create Date: 2025-01-17 14:36:45.488306 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = 'ce28a5ddecee' +down_revision = '28932a02e785' +branch_labels = None +depends_on = None + + +def upgrade(): + with op.batch_alter_table('listing_images', schema=None) as batch_op: + batch_op.create_foreign_key(None, 'listings', ['listing_id'], ['id']) + + with op.batch_alter_table('listings', schema=None) as batch_op: + batch_op.alter_column('depart_time', + existing_type=mysql.DATETIME(), + type_=sa.Time(), + existing_nullable=False) + batch_op.alter_column('destination_time', + existing_type=mysql.DATETIME(), + type_=sa.Time(), + existing_nullable=False)