Enums for choice fields; static files cleaning
8
.gitignore
vendored
|
|
@ -1,15 +1,11 @@
|
||||||
|
.gitignore
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode/
|
.vscode/
|
||||||
env/
|
env/
|
||||||
__pycache__/
|
**/__pycache__
|
||||||
rental/__pycache__/
|
|
||||||
# rental/migrations/
|
|
||||||
villafleurie/__pycache__/
|
|
||||||
villafleurie/staticfiles/
|
|
||||||
villafleurie/static_files/
|
villafleurie/static_files/
|
||||||
rental/client_secrets.json
|
rental/client_secrets.json
|
||||||
token.pickle
|
token.pickle
|
||||||
*.env
|
*.env
|
||||||
*.tar
|
|
||||||
debug.log
|
debug.log
|
||||||
rental/templates/rental/mails
|
rental/templates/rental/mails
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
FROM nginx:latest
|
FROM nginx:latest
|
||||||
|
|
||||||
RUN rm /etc/nginx/conf.d/default.conf
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
COPY nginx/villafleurie.conf /etc/nginx/conf.d/
|
COPY nginx/villafleurie.conf /etc/nginx/conf.d/
|
||||||
|
|
@ -13,19 +13,18 @@ from villafleurie.settings import BASE_DIR
|
||||||
|
|
||||||
def build_calendar_api_service():
|
def build_calendar_api_service():
|
||||||
""" Build Google Calendar API service and returns calendar list and service """
|
""" Build Google Calendar API service and returns calendar list and service """
|
||||||
|
|
||||||
creds = None
|
creds = None
|
||||||
# If modifying these scopes, delete the file token.pickle.
|
|
||||||
SCOPES = [
|
SCOPES = [
|
||||||
'https://www.googleapis.com/auth/calendar',
|
'https://www.googleapis.com/auth/calendar',
|
||||||
'https://www.googleapis.com/auth/calendar.events'
|
'https://www.googleapis.com/auth/calendar.events'
|
||||||
]
|
]
|
||||||
# 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'):
|
if os.path.exists('token.pickle'):
|
||||||
with open('token.pickle', 'rb') as token:
|
with open('token.pickle', 'rb') as token:
|
||||||
creds = pickle.load(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 not creds or not creds.valid:
|
||||||
if creds and creds.expired and creds.refresh_token:
|
if creds and creds.expired and creds.refresh_token:
|
||||||
creds.refresh(Request())
|
creds.refresh(Request())
|
||||||
|
|
@ -34,14 +33,16 @@ def build_calendar_api_service():
|
||||||
flow = InstalledAppFlow.from_client_secrets_file(
|
flow = InstalledAppFlow.from_client_secrets_file(
|
||||||
SECRETS, scopes=SCOPES, redirect_uri="http://localhost:8080/")
|
SECRETS, scopes=SCOPES, redirect_uri="http://localhost:8080/")
|
||||||
creds = flow.run_local_server()
|
creds = flow.run_local_server()
|
||||||
# Save the credentials for the next run
|
|
||||||
with open('token.pickle', 'wb') as token:
|
with open('token.pickle', 'wb') as token:
|
||||||
pickle.dump(creds, token)
|
pickle.dump(creds, token)
|
||||||
|
|
||||||
service = build('calendar', 'v3', credentials=creds)
|
service = build('calendar', 'v3', credentials=creds)
|
||||||
calendars = {
|
calendars = {
|
||||||
'T2': "burik7aclvhc7vsboh06c179uo@group.calendar.google.com",
|
'T2': "burik7aclvhc7vsboh06c179uo@group.calendar.google.com",
|
||||||
'T3': "fu7h30p0gk4a2p4nvo7nsbgpok@group.calendar.google.com"
|
'T3': "fu7h30p0gk4a2p4nvo7nsbgpok@group.calendar.google.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
return service, calendars
|
return service, calendars
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -55,6 +56,7 @@ def get_calendar_reservations(place):
|
||||||
orderBy='startTime'
|
orderBy='startTime'
|
||||||
).execute()
|
).execute()
|
||||||
events = events_result.get('items', [])
|
events = events_result.get('items', [])
|
||||||
|
|
||||||
if not events:
|
if not events:
|
||||||
print('No upcoming events found.')
|
print('No upcoming events found.')
|
||||||
else:
|
else:
|
||||||
|
|
@ -65,6 +67,7 @@ def get_calendar_reservations(place):
|
||||||
'start': event['start'].get('dateTime', event['start'].get('date')).strip(),
|
'start': event['start'].get('dateTime', event['start'].get('date')).strip(),
|
||||||
'end': event['end'].get('dateTime', event['end'].get('date')).strip()
|
'end': event['end'].get('dateTime', event['end'].get('date')).strip()
|
||||||
}
|
}
|
||||||
|
|
||||||
return reservation
|
return reservation
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -72,6 +75,7 @@ def synchronize_calendars(place):
|
||||||
""" Get a complete list of existing bookings in calendar
|
""" Get a complete list of existing bookings in calendar
|
||||||
Creates reservation if not in db, update if already in db
|
Creates reservation if not in db, update if already in db
|
||||||
Delete from db reservation deleted from cal """
|
Delete from db reservation deleted from cal """
|
||||||
|
|
||||||
reservation = get_calendar_reservations(place)
|
reservation = get_calendar_reservations(place)
|
||||||
place = get_object_or_404(Place, name=place.name)
|
place = get_object_or_404(Place, name=place.name)
|
||||||
price = get_reservation_price(
|
price = get_reservation_price(
|
||||||
|
|
@ -81,6 +85,7 @@ def synchronize_calendars(place):
|
||||||
)
|
)
|
||||||
start = reservation['start']
|
start = reservation['start']
|
||||||
end = reservation['end']
|
end = reservation['end']
|
||||||
|
|
||||||
guest = Guest.objects.filter(name=reservation['guest'])
|
guest = Guest.objects.filter(name=reservation['guest'])
|
||||||
if not guest.exists():
|
if not guest.exists():
|
||||||
guest = Guest.objects.create(name=reservation['guest'])
|
guest = Guest.objects.create(name=reservation['guest'])
|
||||||
|
|
@ -108,17 +113,21 @@ def synchronize_calendars(place):
|
||||||
def get_bookings(place):
|
def get_bookings(place):
|
||||||
""" Synchronize with Master calendar via a call to synchronize_calendar
|
""" Synchronize with Master calendar via a call to synchronize_calendar
|
||||||
Returns a list of all related place reservations """
|
Returns a list of all related place reservations """
|
||||||
|
|
||||||
synchronize_calendars(place)
|
synchronize_calendars(place)
|
||||||
booked_dates = Reservation.objects.filter(place=place)
|
booked_dates = Reservation.objects.filter(place=place)
|
||||||
|
|
||||||
return [booking for booking in booked_dates]
|
return [booking for booking in booked_dates]
|
||||||
|
|
||||||
|
|
||||||
def check_availability(place, start_date, end_date):
|
def check_availability(place, start_date, end_date):
|
||||||
""" check if the related place is available during a given period """
|
""" check if the related place is available during a given period """
|
||||||
|
|
||||||
bookings = get_bookings(place)
|
bookings = get_bookings(place)
|
||||||
for booking in bookings:
|
for booking in bookings:
|
||||||
if (booking.start <= start_date <= booking.end) or (booking.start <= end_date <= booking.end):
|
if (booking.start <= start_date <= booking.end) or (booking.start <= end_date <= booking.end):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -142,7 +151,3 @@ def update_calendar(reservation):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
|
|
||||||
# if __name__ == "__main__":
|
|
||||||
# update_calendar(reservation)
|
|
||||||
|
|
|
||||||
8
rental/enums.py
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class PlacesNames(models.TextChoices):
|
||||||
|
|
||||||
|
T2 = "T2", _("T2")
|
||||||
|
T3 = "T3", _("T3")
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from .models import Reservation, Place, Guest
|
from .models import Reservation, Place, Guest
|
||||||
|
from .enums import PlaceNames
|
||||||
|
|
||||||
|
|
||||||
class ReservationForm(forms.Form):
|
class ReservationForm(forms.Form):
|
||||||
PLACES = (('T2', 'T2'), ('T3', 'T3'),)
|
|
||||||
name = forms.CharField(
|
name = forms.CharField(
|
||||||
label="",
|
label="",
|
||||||
max_length=100,
|
max_length=100,
|
||||||
|
|
@ -13,6 +14,7 @@ class ReservationForm(forms.Form):
|
||||||
'placeholder': 'Nom *'
|
'placeholder': 'Nom *'
|
||||||
}),
|
}),
|
||||||
required=True)
|
required=True)
|
||||||
|
|
||||||
email = forms.EmailField(
|
email = forms.EmailField(
|
||||||
label='',
|
label='',
|
||||||
widget=forms.EmailInput(attrs={
|
widget=forms.EmailInput(attrs={
|
||||||
|
|
@ -21,6 +23,7 @@ class ReservationForm(forms.Form):
|
||||||
}),
|
}),
|
||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
|
|
||||||
phone = forms.CharField(
|
phone = forms.CharField(
|
||||||
label='',
|
label='',
|
||||||
max_length=30,
|
max_length=30,
|
||||||
|
|
@ -30,12 +33,16 @@ class ReservationForm(forms.Form):
|
||||||
'placeholder': 'Téléphone *'
|
'placeholder': 'Téléphone *'
|
||||||
}),
|
}),
|
||||||
required=True)
|
required=True)
|
||||||
|
|
||||||
place = forms.ChoiceField(
|
place = forms.ChoiceField(
|
||||||
label='',
|
label='',
|
||||||
widget=forms.Select(
|
widget=forms.Select(
|
||||||
attrs={'class': 'form-control form-control-lg form-control-a'}),
|
attrs={'class': 'form-control form-control-lg form-control-a'}),
|
||||||
required=True,
|
required=True,
|
||||||
choices=PLACES)
|
choices=PlacesNames.choices,
|
||||||
|
default=PlacesNames.T2
|
||||||
|
)
|
||||||
|
|
||||||
message = forms.CharField(
|
message = forms.CharField(
|
||||||
label='',
|
label='',
|
||||||
# max_length=100,
|
# max_length=100,
|
||||||
|
|
@ -47,6 +54,7 @@ class ReservationForm(forms.Form):
|
||||||
'placeholder': 'Message *'
|
'placeholder': 'Message *'
|
||||||
}),
|
}),
|
||||||
required=True)
|
required=True)
|
||||||
|
|
||||||
start = forms.DateField(
|
start = forms.DateField(
|
||||||
label='',
|
label='',
|
||||||
input_formats=['%d/%m/%Y'],
|
input_formats=['%d/%m/%Y'],
|
||||||
|
|
@ -56,6 +64,7 @@ class ReservationForm(forms.Form):
|
||||||
'class': 'form-control form-control-lg form-control-a',
|
'class': 'form-control form-control-lg form-control-a',
|
||||||
'placeholder': '01/01/2020 *'}),
|
'placeholder': '01/01/2020 *'}),
|
||||||
required=True)
|
required=True)
|
||||||
|
|
||||||
end = forms.DateField(
|
end = forms.DateField(
|
||||||
label='',
|
label='',
|
||||||
input_formats=['%d/%m/%Y'],
|
input_formats=['%d/%m/%Y'],
|
||||||
|
|
@ -66,6 +75,7 @@ class ReservationForm(forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class ContactForm(forms.Form):
|
class ContactForm(forms.Form):
|
||||||
|
|
||||||
name = forms.CharField(
|
name = forms.CharField(
|
||||||
label='',
|
label='',
|
||||||
widget=forms.TextInput(attrs={
|
widget=forms.TextInput(attrs={
|
||||||
|
|
@ -74,6 +84,7 @@ class ContactForm(forms.Form):
|
||||||
}),
|
}),
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
email = forms.EmailField(
|
email = forms.EmailField(
|
||||||
label='',
|
label='',
|
||||||
widget=forms.EmailInput(attrs={
|
widget=forms.EmailInput(attrs={
|
||||||
|
|
@ -82,6 +93,7 @@ class ContactForm(forms.Form):
|
||||||
}),
|
}),
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
subject = forms.CharField(
|
subject = forms.CharField(
|
||||||
label='',
|
label='',
|
||||||
widget=forms.TextInput(attrs={
|
widget=forms.TextInput(attrs={
|
||||||
|
|
@ -90,6 +102,7 @@ class ContactForm(forms.Form):
|
||||||
}),
|
}),
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
message = forms.CharField(
|
message = forms.CharField(
|
||||||
label='',
|
label='',
|
||||||
# max_length=100,
|
# max_length=100,
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@ from datetime import datetime
|
||||||
|
|
||||||
def get_reservation_price(place, start, end):
|
def get_reservation_price(place, start, end):
|
||||||
""" Compute booking price as a function of place and dates """
|
""" Compute booking price as a function of place and dates """
|
||||||
|
|
||||||
if type(start) == str:
|
if type(start) == str:
|
||||||
start = datetime.strptime(start, '%Y-%m-%d')
|
start = datetime.strptime(start, '%Y-%m-%d')
|
||||||
if type(end) == str:
|
if type(end) == str:
|
||||||
end = datetime.strptime(end, '%Y-%m-%d')
|
end = datetime.strptime(end, '%Y-%m-%d')
|
||||||
nights = (end - start).days
|
nights = (end - start).days
|
||||||
|
|
||||||
return place.price * nights
|
return place.price * nights
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 5 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 3 KiB |
|
Before Width: | Height: | Size: 3 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 255 KiB |
|
Before Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 58 KiB |
1197
rental/static/rental/lib/popper/popper.min.js
vendored
|
|
@ -1,36 +1,37 @@
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
|
|
||||||
from .models import Testimonial, Reservation, Guest, Place
|
|
||||||
from django.urls import reverse_lazy
|
|
||||||
from django.views.generic.base import TemplateView
|
|
||||||
from .forms import ReservationForm, ContactForm
|
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
|
from django.views.generic.base import TemplateView
|
||||||
|
from rental.forms import ReservationForm, ContactForm
|
||||||
|
from rental.models import Testimonial, Reservation, Guest, Place, Contact
|
||||||
from rental.pricing import get_reservation_price
|
from rental.pricing import get_reservation_price
|
||||||
from rental.bookings import check_availability, synchronize_calendars, update_calendar
|
from rental.bookings import check_availability, synchronize_calendars, update_calendar
|
||||||
# send_confirmation_mail, send_notification, send_quotation
|
from rental.tasks.apiMailer import * # or gMailer
|
||||||
from rental.tasks.apiMailer import * # gMailer
|
|
||||||
from rental.models import Contact
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
places = Place.objects.all()
|
places = Place.objects.all()
|
||||||
temoignages = Testimonial.objects.all()
|
temoignages = Testimonial.objects.all()
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'places': places,
|
'places': places,
|
||||||
'temoignages': temoignages
|
'temoignages': temoignages
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, 'rental/index.html', context)
|
return render(request, 'rental/index.html', context)
|
||||||
|
|
||||||
|
|
||||||
def liste_location(request):
|
def liste_location(request):
|
||||||
places = Place.objects.all()
|
places = Place.objects.all()
|
||||||
|
|
||||||
context = {'places': places}
|
context = {'places': places}
|
||||||
|
|
||||||
return render(request, 'rental/list_place.html', context)
|
return render(request, 'rental/list_place.html', context)
|
||||||
|
|
||||||
|
|
||||||
def location(request, place_name='T2'):
|
def location(request, place_name='T2'):
|
||||||
place = get_object_or_404(Place, name=place_name)
|
place = get_object_or_404(Place, name=place_name)
|
||||||
images = place.images.all()
|
images = place.images.all()
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'place': place,
|
'place': place,
|
||||||
'images': images
|
'images': images
|
||||||
|
|
@ -44,6 +45,7 @@ def location(request, place_name='T2'):
|
||||||
|
|
||||||
def reservation(request):
|
def reservation(request):
|
||||||
context, template = handle_reservation_form(request)
|
context, template = handle_reservation_form(request)
|
||||||
|
|
||||||
return render(request, template, context)
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -58,6 +60,7 @@ def handle_reservation_form(request, context={}, init_template='rental/reservati
|
||||||
place_name = form.cleaned_data['place']
|
place_name = form.cleaned_data['place']
|
||||||
start = form.cleaned_data['start']
|
start = form.cleaned_data['start']
|
||||||
end = form.cleaned_data['end']
|
end = form.cleaned_data['end']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
guest = Guest.objects.filter(email=email)
|
guest = Guest.objects.filter(email=email)
|
||||||
if not guest.exists():
|
if not guest.exists():
|
||||||
|
|
@ -68,6 +71,7 @@ def handle_reservation_form(request, context={}, init_template='rental/reservati
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
guest = guest.first()
|
guest = guest.first()
|
||||||
|
|
||||||
place = get_object_or_404(Place, name=place_name)
|
place = get_object_or_404(Place, name=place_name)
|
||||||
available = check_availability(place, start, end)
|
available = check_availability(place, start, end)
|
||||||
price = get_reservation_price(place, start, end)
|
price = get_reservation_price(place, start, end)
|
||||||
|
|
@ -91,13 +95,16 @@ def handle_reservation_form(request, context={}, init_template='rental/reservati
|
||||||
context = {'form': form}
|
context = {'form': form}
|
||||||
template = 'rental/reservation.html'
|
template = 'rental/reservation.html'
|
||||||
return context, template
|
return context, template
|
||||||
|
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
form.errors['internal'] = "Une erreur interne est apparue. Merci de recommencer votre requête."
|
form.errors['internal'] = "Une erreur interne est apparue. Merci de recommencer votre requête."
|
||||||
else:
|
else:
|
||||||
form = ReservationForm()
|
form = ReservationForm()
|
||||||
|
|
||||||
context['form'] = form
|
context['form'] = form
|
||||||
context['errors'] = form.errors.items()
|
context['errors'] = form.errors.items()
|
||||||
template = init_template
|
template = init_template
|
||||||
|
|
||||||
return context, template
|
return context, template
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -113,6 +120,7 @@ def calendar(request, place_name):
|
||||||
'place_name': place_name,
|
'place_name': place_name,
|
||||||
'bookings': bookings
|
'bookings': bookings
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, 'rental/calendar.html', context)
|
return render(request, 'rental/calendar.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||