mirror of
https://github.com/rjNemo/villafleurie
synced 2026-06-12 13:26:47 +00:00
minor advance in booking system
This commit is contained in:
parent
c412e2abdc
commit
527e7fd973
7 changed files with 291 additions and 143 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -8,4 +8,5 @@ rental/static/
|
|||
.vscode/
|
||||
Procfile
|
||||
heroku.yml
|
||||
rental/client_secrets.json
|
||||
|
||||
|
|
|
|||
1
calendar.dat
Normal file
1
calendar.dat
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"access_token": "ya29.Il-0B4bh7Yq32B8mvoTT0EiAbYtzr1snXN_AwfcJzzaXu_L74OpPkkH7UgxQIzh3ZkiF_7fQq5VwZ5lIPE8ag5iei_D58rDeFdvT6x3V0MC9uOAyTBsejAlh2aJl9hY1Lg", "client_id": "1068382961407-1dudhnc4e9d7ham53nr6b7ak3c4dlvme.apps.googleusercontent.com", "client_secret": "F9zmMyRRiTUEAMfRiv8A2l1d", "refresh_token": "1//09MUSHKoeusLDCgYIARAAGAkSNwF-L9IrNOqe0TCFB9n1MZZwuAQNtfkdXAv7x4bUs2AxMwfmTdccxSMhCMEwjaXRUD2WR_EXiss", "token_expiry": "2019-12-06T16:13:57Z", "token_uri": "https://accounts.google.com/o/oauth2/token", "user_agent": null, "revoke_uri": "https://oauth2.googleapis.com/revoke", "id_token": null, "id_token_jwt": null, "token_response": {"access_token": "ya29.Il-0B4bh7Yq32B8mvoTT0EiAbYtzr1snXN_AwfcJzzaXu_L74OpPkkH7UgxQIzh3ZkiF_7fQq5VwZ5lIPE8ag5iei_D58rDeFdvT6x3V0MC9uOAyTBsejAlh2aJl9hY1Lg", "expires_in": 3600, "scope": "https://www.googleapis.com/auth/calendar.readonly", "token_type": "Bearer"}, "scopes": ["https://www.googleapis.com/auth/calendar.readonly"], "token_info_uri": "https://oauth2.googleapis.com/tokeninfo", "invalid": false, "_class": "OAuth2Credentials", "_module": "oauth2client.client"}
|
||||
138
rental/bookings.py
Normal file
138
rental/bookings.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
from __future__ import print_function
|
||||
from googleapiclient import sample_tools
|
||||
from google.auth.transport.requests import Request
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from googleapiclient.discovery import build
|
||||
import os.path
|
||||
import pickle
|
||||
import datetime
|
||||
from oauth2client import client
|
||||
import sys
|
||||
# from rental.models import Reservation
|
||||
|
||||
|
||||
def get_bookings(place):
|
||||
"""
|
||||
returns a list of all related place reservations
|
||||
"""
|
||||
booked_dates = Reservation.objects.all()
|
||||
return [booking for booking in booked_dates if booking.place.name == f"{place.name}"]
|
||||
|
||||
|
||||
def check_availability(place, start_date, end_date):
|
||||
"""
|
||||
check if the related place is available during a given period
|
||||
"""
|
||||
bookings = get_bookings(place)
|
||||
for booking in bookings:
|
||||
if (booking.start <= start_date <= booking.end) or (booking.start <= end_date <= booking.end):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
"""Simple command-line sample for the Calendar API.
|
||||
Command-line application that retrieves the list of the user's calendars."""
|
||||
|
||||
|
||||
def main1(argv):
|
||||
# Authenticate and construct service.
|
||||
service, _ = sample_tools.init(
|
||||
argv, 'calendar', 'v3', __doc__, __file__,
|
||||
scope='https://www.googleapis.com/auth/calendar.readonly')
|
||||
|
||||
try:
|
||||
page_token = None
|
||||
while True:
|
||||
calendar_list = service.calendarList().list(
|
||||
pageToken=page_token).execute()
|
||||
for calendar_list_entry in calendar_list['items']:
|
||||
print(calendar_list_entry['summary'])
|
||||
page_token = calendar_list.get('nextPageToken')
|
||||
|
||||
if not page_token:
|
||||
break
|
||||
|
||||
# Call the Calendar API
|
||||
calendar_list = service.calendarList().list(
|
||||
pageToken=page_token).execute()
|
||||
# print(calendar_list)
|
||||
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
|
||||
print('Getting the upcoming 10 T2 arrivals')
|
||||
|
||||
events_result = service.events().list(calendarId='burik7aclvhc7vsboh06c179uo@group.calendar.google.com', timeMin=now,
|
||||
maxResults=10, singleEvents=True,
|
||||
orderBy='startTime').execute()
|
||||
events = events_result.get('items', [])
|
||||
|
||||
if not events:
|
||||
print('No upcoming events found.')
|
||||
for event in events:
|
||||
start = event['start'].get('dateTime', event['start'].get('date'))
|
||||
end = event['end'].get('dateTime', event['end'].get('date'))
|
||||
print(start, end, event['summary'])
|
||||
|
||||
print('Getting the upcoming 10 T3 arrivals')
|
||||
events_result = service.events().list(calendarId='fu7h30p0gk4a2p4nvo7nsbgpok@group.calendar.google.com', timeMin=now,
|
||||
maxResults=10, singleEvents=True,
|
||||
orderBy='startTime').execute()
|
||||
events = events_result.get('items', [])
|
||||
|
||||
if not events:
|
||||
print('No upcoming events found.')
|
||||
for event in events:
|
||||
start = event['start'].get('dateTime', event['start'].get('date'))
|
||||
end = event['end'].get('dateTime', event['end'].get('date'))
|
||||
print(start, end, event['summary'])
|
||||
|
||||
except client.AccessTokenRefreshError:
|
||||
print('The credentials have been revoked or expired, please re-run'
|
||||
'the application to re-authorize.')
|
||||
|
||||
|
||||
# If modifying these scopes, delete the file token.pickle.
|
||||
# SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
|
||||
|
||||
|
||||
# def main():
|
||||
# """Shows basic usage of the Google Calendar API.
|
||||
# Prints the start and name of the next 10 events on the user's calendar.
|
||||
# """
|
||||
# creds = None
|
||||
# # The file token.pickle stores the user's access and refresh tokens, and is
|
||||
# # created automatically when the authorization flow completes for the first
|
||||
# # time.
|
||||
# if os.path.exists('token.pickle'):
|
||||
# with open('token.pickle', 'rb') as token:
|
||||
# creds = pickle.load(token)
|
||||
# # If there are no (valid) credentials available, let the user log in.
|
||||
# if not creds or not creds.valid:
|
||||
# if creds and creds.expired and creds.refresh_token:
|
||||
# creds.refresh(Request())
|
||||
# else:
|
||||
# flow = InstalledAppFlow.from_client_secrets_file(
|
||||
# 'credentials.json', SCOPES)
|
||||
# creds = flow.run_local_server(port=0)
|
||||
# # Save the credentials for the next run
|
||||
# with open('token.pickle', 'wb') as token:
|
||||
# pickle.dump(creds, token)
|
||||
|
||||
# service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
# # Call the Calendar API
|
||||
# now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
|
||||
# print('Getting the upcoming 10 events')
|
||||
# events_result = service.events().list(calendarId='primary', timeMin=now,
|
||||
# maxResults=10, singleEvents=True,
|
||||
# orderBy='startTime').execute()
|
||||
# events = events_result.get('items', [])
|
||||
|
||||
# if not events:
|
||||
# print('No upcoming events found.')
|
||||
# for event in events:
|
||||
# start = event['start'].get('dateTime', event['start'].get('date'))
|
||||
# print(start, event['summary'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main1(sys.argv)
|
||||
# main()
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
from rental.models import Reservation
|
||||
|
||||
def get_bookings(place):
|
||||
"""
|
||||
returns a list of all related place reservations
|
||||
"""
|
||||
booked_dates = Reservation.objects.all()
|
||||
return [booking for booking in booked_dates if booking.place.name == f"{place.name}"]
|
||||
|
||||
|
||||
def check_availability(place, start_date, end_date):
|
||||
"""
|
||||
check if the related place is available during a given period
|
||||
"""
|
||||
bookings = get_bookings(place)
|
||||
for booking in bookings:
|
||||
if (booking.start <= start_date <= booking.end) or (booking.start <= end_date <= booking.end):
|
||||
return False
|
||||
return True
|
||||
|
|
@ -28,6 +28,7 @@ class Place(models.Model):
|
|||
thumbnail = models.ForeignKey(
|
||||
Image, on_delete=models.CASCADE, blank=True, null=True)
|
||||
images = models.ManyToManyField(Image, related_name="places", blank=True)
|
||||
calendar = models.CharField(max_length=350, blank=True, null=True)
|
||||
|
||||
|
||||
class Guest(models.Model):
|
||||
|
|
|
|||
|
|
@ -1,154 +1,180 @@
|
|||
{% extends 'rental/base.html'%}
|
||||
{% load static %}
|
||||
{% extends 'rental/base.html'%} {% load static %} {% block content %}
|
||||
|
||||
{% block content %}
|
||||
<!--/ Intro Single star /-->
|
||||
<section class="intro-single">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-lg-8">
|
||||
<div class="title-single-box">
|
||||
<h1 class="title-single">{{ place.subname }} {{ place.name }} </h1>
|
||||
<span class="color-text-a">VillaFleurie</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12 col-lg-4">
|
||||
<nav aria-label="breadcrumb" class="breadcrumb-box d-flex justify-content-lg-end">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'rental:index' %}">Accueil</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'rental:list_place' %}">Hébergements</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active" aria-current="page">
|
||||
{{ place.name }}
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<section class="intro-single">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-lg-8">
|
||||
<div class="title-single-box">
|
||||
<h1 class="title-single">{{ place.subname }} {{ place.name }}</h1>
|
||||
<span class="color-text-a">VillaFleurie</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12 col-lg-4">
|
||||
<nav
|
||||
aria-label="breadcrumb"
|
||||
class="breadcrumb-box d-flex justify-content-lg-end"
|
||||
>
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'rental:index' %}">Accueil</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'rental:list_place' %}">Hébergements</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active" aria-current="page">
|
||||
{{ place.name }}
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!--/ Intro Single End /-->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--/ Property Single Star /-->
|
||||
<section class="property-single nav-arrow-b">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div id="property-single-carousel" class="owl-carousel owl-arrow gallery-property">
|
||||
{% for image in images %}
|
||||
<div class="carousel-item-b">
|
||||
<img src="{{ image.img.url }}" alt="{{image.alt}}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
<section class="property-single nav-arrow-b">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div
|
||||
id="property-single-carousel"
|
||||
class="owl-carousel owl-arrow gallery-property"
|
||||
>
|
||||
{% for image in images %}
|
||||
<div class="carousel-item-b">
|
||||
<img src="{{ image.img.url }}" alt="{{image.alt}}" />
|
||||
</div>
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-md-5 col-lg-4">
|
||||
<div class="property-price d-flex justify-content-center foo">
|
||||
<div class="card-header-c d-flex">
|
||||
<div class="card-box-ico">
|
||||
<span class="ion-money">€</span>
|
||||
</div>
|
||||
<div class="card-title-c align-self-center">
|
||||
<h5 class="title-c"> {{ place.price }} </h5>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-md-5 col-lg-4">
|
||||
<div class="property-price d-flex justify-content-center foo">
|
||||
<div class="card-header-c d-flex">
|
||||
<div class="card-box-ico">
|
||||
<span class="ion-money">€</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="property-summary">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="col-md-12 mb-1">
|
||||
<a href="#reservation"><button class="btn btn-a">Réserver maintenant</button></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="property-summary">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="title-box-d section-t4">
|
||||
<h3 class="title-d">Résumé</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-list">
|
||||
<ul class="list">
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Localisation :</strong>
|
||||
<span>Le Gosier, Guadeloupe</span>
|
||||
</li>
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Status :</strong>
|
||||
<span>Disponible</span>
|
||||
</li>
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Surface :</strong>
|
||||
<span>{{ place.surface}}
|
||||
<sup>2</sup>
|
||||
</span>
|
||||
</li>
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Lits :</strong>
|
||||
<span>{{ place.beds }}</span>
|
||||
</li>
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Nombre de voyageurs maximum :</strong>
|
||||
<span>{{ place.max_occupation}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="card-title-c align-self-center">
|
||||
<h5 class="title-c">{{ place.price }}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7 col-lg-7 section-md-t3">
|
||||
<div class="col-sm-12">
|
||||
<div class="title-box-d section-t4">
|
||||
<a href="#reservation" class="btn btn-a">Réserver</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="property-summary">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="title-box-d">
|
||||
<h3 class="title-d">Description</h3>
|
||||
<div class="title-box-d section-t4">
|
||||
<h3 class="title-d">Résumé</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="property-description">
|
||||
<p class="description color-text-a">
|
||||
{{ place.description |safe}}
|
||||
</p>
|
||||
<p class="description color-text-a no-margin">
|
||||
{{ place.info |safe}}
|
||||
</p>
|
||||
<div class="summary-list">
|
||||
<ul class="list">
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Localisation :</strong>
|
||||
<span>Le Gosier, Guadeloupe</span>
|
||||
</li>
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Status :</strong>
|
||||
<span>Disponible</span>
|
||||
</li>
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Surface :</strong>
|
||||
<span
|
||||
>{{ place.surface}}
|
||||
<sup>2</sup>
|
||||
</span>
|
||||
</li>
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Lits :</strong>
|
||||
<span>{{ place.beds }}</span>
|
||||
</li>
|
||||
<li class="d-flex justify-content-between">
|
||||
<strong>Nombre de voyageurs maximum :</strong>
|
||||
<span>{{ place.max_occupation}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<iframe
|
||||
src="{{place.calendar | safe}}"
|
||||
style="border-width:0"
|
||||
width="400"
|
||||
height="400"
|
||||
frameborder="0"
|
||||
scrolling="no"
|
||||
></iframe>
|
||||
</div>
|
||||
<div class="col-md-7 col-lg-7 section-md-t3">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="title-box-d">
|
||||
<h3 class="title-d">Description</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="property-description">
|
||||
<p class="description color-text-a">
|
||||
{{ place.description |safe}}
|
||||
</p>
|
||||
<p class="description color-text-a no-margin">
|
||||
{{ place.info |safe}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-10 offset-md-1">
|
||||
<ul class="nav nav-pills-a nav-pills mb-3 section-t3" id="pills-tab" role="tablist">
|
||||
<!-- <li class="nav-item">
|
||||
</div>
|
||||
<div class="col-md-10 offset-md-1">
|
||||
<ul
|
||||
class="nav nav-pills-a nav-pills mb-3 section-t3"
|
||||
id="pills-tab"
|
||||
role="tablist"
|
||||
>
|
||||
<!-- <li class="nav-item">
|
||||
<a class="nav-link active" id="pills-video-tab" data-toggle="pill" href="#pills-video" role="tab"
|
||||
aria-controls="pills-video" aria-selected="true">Video</a>
|
||||
</li> -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="pills-map-tab" data-toggle="pill" href="#pills-map" role="tab" aria-controls="pills-map"
|
||||
aria-selected="false">Localisation</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="pills-tabContent">
|
||||
<!-- <div class="tab-pane fade show active" id="pills-video" role="tabpanel" aria-labelledby="pills-video-tab">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link active"
|
||||
id="pills-map-tab"
|
||||
data-toggle="pill"
|
||||
href="#pills-map"
|
||||
role="tab"
|
||||
aria-controls="pills-map"
|
||||
aria-selected="false"
|
||||
>Localisation</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="pills-tabContent">
|
||||
<!-- <div class="tab-pane fade show active" id="pills-video" role="tabpanel" aria-labelledby="pills-video-tab">
|
||||
<iframe src="https://player.vimeo.com/video/73221098" width="100%" height="460" frameborder="0"
|
||||
webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
|
||||
</div> -->
|
||||
<div class="tab-pane fade show active" id="pills-map" role="tabpanel" aria-labelledby="pills-map-tab">
|
||||
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3831.2547874074585!2d-61.48954768556058!3d16.207335988795712!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x8c134f148764f5d5%3A0x981bb218cee8b16c!2sVillaFleurie!5e0!3m2!1sfr!2sde!4v1573125589212!5m2!1sfr!2sde"
|
||||
width="100%" height="460" frameborder="0" style="border:0;" allowfullscreen=""></iframe>
|
||||
</div>
|
||||
<div
|
||||
class="tab-pane fade show active"
|
||||
id="pills-map"
|
||||
role="tabpanel"
|
||||
aria-labelledby="pills-map-tab"
|
||||
>
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3831.2547874074585!2d-61.48954768556058!3d16.207335988795712!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x8c134f148764f5d5%3A0x981bb218cee8b16c!2sVillaFleurie!5e0!3m2!1sfr!2sde!4v1573125589212!5m2!1sfr!2sde"
|
||||
width="100%"
|
||||
height="460"
|
||||
frameborder="0"
|
||||
style="border:0;"
|
||||
allowfullscreen=""
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'rental/reservation_form.html' %}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% include 'rental/reservation_form.html' %}
|
||||
</div>
|
||||
</section>
|
||||
<!--/ Property Single End /-->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from django.views.generic.base import TemplateView
|
|||
from .forms import ReservationForm
|
||||
from django.db import IntegrityError
|
||||
from rental.pricing import get_reservation_price
|
||||
from rental.calendar import check_availability
|
||||
from rental.bookings import check_availability
|
||||
|
||||
|
||||
def index(request):
|
||||
|
|
|
|||
Loading…
Reference in a new issue