firrst commit
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_bcrypt import Bcrypt
|
||||
from flask_login import LoginManager
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
|
||||
app.config['SECRET_KEY'] = 'supersecretkey'
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
bcrypt = Bcrypt(app)
|
||||
login_manager = LoginManager(app)
|
||||
login_manager.login_view = 'login'
|
||||
|
||||
from routes import *
|
||||
from models import *
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
@@ -0,0 +1,36 @@
|
||||
from app import db
|
||||
from flask_login import UserMixin
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
email = db.Column(db.String(150), unique=True, nullable=False)
|
||||
password = db.Column(db.String(60), nullable=False)
|
||||
phone = db.Column(db.String(20), nullable=False)
|
||||
is_host = db.Column(db.Boolean, default=False)
|
||||
|
||||
class Listing(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
title = db.Column(db.String(150), nullable=False)
|
||||
description = db.Column(db.Text, nullable=False)
|
||||
price = db.Column(db.Float, nullable=False)
|
||||
location = db.Column(db.String(150), nullable=False)
|
||||
property_type = db.Column(db.String(50), nullable=False)
|
||||
host_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
images = db.relationship('Image', backref='listing', lazy=True)
|
||||
latitude = db.Column(db.Float, nullable=True)
|
||||
longitude = db.Column(db.Float, nullable=True)
|
||||
|
||||
class Image(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
url = db.Column(db.String(300), nullable=False)
|
||||
listing_id = db.Column(db.Integer, db.ForeignKey('listing.id'), nullable=False)
|
||||
|
||||
class Booking(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
guest_name = db.Column(db.String(100), nullable=False)
|
||||
email = db.Column(db.String(120), nullable=False)
|
||||
phone = db.Column(db.String(20), nullable=False)
|
||||
listing_id = db.Column(db.Integer, db.ForeignKey('listing.id'), nullable=False)
|
||||
check_in = db.Column(db.DateTime, nullable=False)
|
||||
check_out = db.Column(db.DateTime, nullable=False)
|
||||
guests = db.Column(db.Integer, nullable=False)
|
||||
@@ -0,0 +1,94 @@
|
||||
from flask import render_template, request, redirect, url_for
|
||||
from flask_login import login_user, login_required, logout_user, current_user
|
||||
from app import app, db, bcrypt, login_manager
|
||||
from models import User, Listing, Booking, Image
|
||||
from datetime import datetime
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User.query.get(int(user_id))
|
||||
|
||||
@app.route('/')
|
||||
def home():
|
||||
property_type = request.args.get('property_type')
|
||||
listings = Listing.query
|
||||
available_listings = []
|
||||
for listing in listings:
|
||||
bookings = Booking.query.filter_by(listing_id=listing.id).all()
|
||||
if all(datetime.strptime(booking.check_out, '%Y-%m-%d') < datetime.now() for booking in bookings):
|
||||
available_listings.append(listing)
|
||||
if property_type:
|
||||
available_listings = [l for l in available_listings if l.property_type == property_type]
|
||||
return render_template('index.html', listings=available_listings)
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
email = request.form['email']
|
||||
password = request.form['password']
|
||||
user = User.query.filter_by(email=email).first()
|
||||
if user and bcrypt.check_password_hash(user.password, password):
|
||||
login_user(user)
|
||||
return redirect(url_for('dashboard'))
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
email = request.form['email']
|
||||
password = bcrypt.generate_password_hash(request.form['password']).decode('utf-8')
|
||||
phone = request.form['phone']
|
||||
user = User(email=email, password=password, phone=phone)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
login_user(user)
|
||||
return redirect(url_for('dashboard'))
|
||||
return render_template('register.html')
|
||||
|
||||
@app.route('/dashboard')
|
||||
@login_required
|
||||
def dashboard():
|
||||
if current_user.is_host:
|
||||
listings = Listing.query.filter_by(host_id=current_user.id).all()
|
||||
return render_template('dashboard.html', listings=listings)
|
||||
return redirect(url_for('home'))
|
||||
|
||||
@app.route('/create-listing', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def create_listing():
|
||||
if request.method == 'POST':
|
||||
title = request.form['title']
|
||||
description = request.form['description']
|
||||
price = request.form['price']
|
||||
location = request.form['location']
|
||||
property_type = request.form['property_type']
|
||||
latitude = request.form.get('latitude')
|
||||
longitude = request.form.get('longitude')
|
||||
listing = Listing(title=title, description=description, price=price, location=location, property_type=property_type, host_id=current_user.id, latitude=latitude, longitude=longitude)
|
||||
db.session.add(listing)
|
||||
db.session.flush()
|
||||
|
||||
image_urls = request.form.getlist('images')
|
||||
for url in image_urls:
|
||||
image = Image(url=url, listing_id=listing.id)
|
||||
db.session.add(image)
|
||||
|
||||
db.session.commit()
|
||||
return redirect(url_for('dashboard'))
|
||||
return render_template('create_listing.html')
|
||||
|
||||
@app.route('/booking/<int:listing_id>', methods=['GET', 'POST'])
|
||||
def booking(listing_id):
|
||||
listing = Listing.query.get_or_404(listing_id)
|
||||
if request.method == 'POST':
|
||||
guest_name = request.form['name']
|
||||
email = request.form['email']
|
||||
phone = request.form['phone']
|
||||
check_in = datetime.strptime(request.form['check_in'], '%Y-%m-%d')
|
||||
check_out = datetime.strptime(request.form['check_out'], '%Y-%m-%d')
|
||||
guests = request.form['guests']
|
||||
booking = Booking(guest_name=guest_name, email=email, phone=phone, listing_id=listing_id, check_in=check_in, check_out=check_out, guests=guests)
|
||||
db.session.add(booking)
|
||||
db.session.commit()
|
||||
return redirect(url_for('home'))
|
||||
return render_template('booking.html', listing=listing)
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Booking System{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">Booking System</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
{% if current_user.is_authenticated %}
|
||||
<li class="nav-item"><a class="nav-link" href="/dashboard">Dashboard</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/logout">Logout</a></li>
|
||||
{% else %}
|
||||
<li class="nav-item"><a class="nav-link" href="/login">Login</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/register">Register</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container mt-4">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,37 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Booking{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h1 class="text-center">Book {{ listing.title }}</h1>
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Full Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="phone" class="form-label">Phone</label>
|
||||
<input type="text" class="form-control" id="phone" name="phone" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="check_in" class="form-label">Check-in Date</label>
|
||||
<input type="date" class="form-control" id="check_in" name="check_in" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="check_out" class="form-label">Check-out Date</label>
|
||||
<input type="date" class="form-control" id="check_out" name="check_out" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="guests" class="form-label">Number of Guests</label>
|
||||
<input type="number" class="form-control" id="guests" name="guests" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Confirm Booking</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,40 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Create Listing{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h1 class="text-center">Create Listing</h1>
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<input type="text" class="form-control" id="title" name="title" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="3" required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="price" class="form-label">Price</label>
|
||||
<input type="number" class="form-control" id="price" name="price" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="location" class="form-label">Location</label>
|
||||
<input type="text" class="form-control" id="location" name="location" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="property_type" class="form-label">Property Type</label>
|
||||
<input type="text" class="form-control" id="property_type" name="property_type" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="latitude" class="form-label">Latitude</label>
|
||||
<input type="text" class="form-control" id="latitude" name="latitude">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="longitude" class="form-label">Longitude</label>
|
||||
<input type="text" class="form-control" id="longitude" name="longitude">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Create</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,20 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Dashboard{% endblock %}
|
||||
{% block content %}
|
||||
<h1 class="text-center">Your Listings</h1>
|
||||
<div class="row">
|
||||
{% for listing in listings %}
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100">
|
||||
<img src="/static/images/default.jpg" class="card-img-top" alt="...">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ listing.title }}</h5>
|
||||
<p class="card-text">{{ listing.description }}</p>
|
||||
<a href="/edit-listing/{{ listing.id }}" class="btn btn-warning">Edit</a>
|
||||
<a href="/delete-listing/{{ listing.id }}" class="btn btn-danger">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Home{% endblock %}
|
||||
{% block content %}
|
||||
<h1 class="text-center">Available Listings</h1>
|
||||
<div class="row">
|
||||
{% for listing in listings %}
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100">
|
||||
<img src="/static/images/default.jpg" class="card-img-top" alt="...">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ listing.title }}</h5>
|
||||
<p class="card-text">{{ listing.description }}</p>
|
||||
<a href="/booking/{{ listing.id }}" class="btn btn-primary">Book Now</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,20 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Login{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h1 class="text-center">Login</h1>
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Register{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h1 class="text-center">Register</h1>
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="phone" class="form-label">Phone</label>
|
||||
<input type="text" class="form-control" id="phone" name="phone" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success w-100">Register</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user