doc: add doctrings

This commit is contained in:
Ruidy Nemausat 2020-07-26 11:30:26 +02:00
parent 5eea1e12ee
commit 2e690ae9a3
22 changed files with 121 additions and 59 deletions

View file

@ -1,3 +1,5 @@
""" Register models and configure admin dashboard """
from django.contrib import admin from django.contrib import admin
from rental.models.booking import Booking from rental.models.booking import Booking

View file

@ -3,5 +3,8 @@ from django.utils.translation import gettext_lazy as _
class PlaceNames(models.TextChoices): class PlaceNames(models.TextChoices):
""" PlaceNames enums serve to list places and prepare
for internationalisation
"""
T2 = "T2", _("T2") T2 = "T2", _("T2")
T3 = "T3", _("T3") T3 = "T3", _("T3")

View file

@ -1,11 +1,13 @@
from django import forms from django import forms
from rental.enums import PlaceNames from rental.enums import PlaceNames
from rental.models.booking import Booking
from rental.models.place import Place
class BookingForm(forms.Form): class BookingForm(forms.Form):
"""
BookingForm serve to validate input and create a new booking.
doc: https://docs.djangoproject.com/fr/2.2/topics/forms/modelforms/
"""
name = forms.CharField( name = forms.CharField(
label="", label="",

View file

@ -1,10 +1,11 @@
from django import forms from django import forms
from rental.models.contact import Contact
from rental.models.place import Place
class ContactForm(forms.Form): class ContactForm(forms.Form):
"""
ContactForm serve to validate input and create a new contact.
doc: https://docs.djangoproject.com/fr/2.2/topics/forms/modelforms/
"""
name = forms.CharField( name = forms.CharField(
label='', label='',

View file

@ -3,26 +3,33 @@ from datetime import datetime
from django.db import models from django.db import models
import rental.services.calendar as calendar import rental.services.calendar as calendar
import rental.tasks.apiMailer as mailer # or gMailer import rental.tasks.api_mailer as mailer # or g_mailer
from rental.models.guest import Guest from rental.models.guest import Guest
from rental.models.place import Place from rental.models.place import Place
class BookingManager(models.Manager): class BookingManager(models.Manager):
""" BookingManager is the interface through which database query operations """
are provided to Django models. BookingManager is the interface through which database query operations
are provided to Django models.
""" """
def create_booking(self, **kwargs): def create_booking(self, **kwargs):
""" create_booking creates a Booking instance, computes the price and """
updates the remote calendar. """ Create_booking creates a Booking instance, computes the price and
updates the remote calendar.
"""
booking = self.create(**kwargs) booking = self.create(**kwargs)
booking.price = booking.get_price() booking.price = booking.get_price()
calendar.update(booking) calendar.update(booking)
return booking return booking
class Booking(models.Model): class Booking(models.Model):
""" Booking encapsulate booking instance information """
class Meta: class Meta:
verbose_name = 'Réservation' verbose_name = 'Réservation'
@ -39,15 +46,17 @@ class Booking(models.Model):
price = models.DecimalField(max_digits=6, decimal_places=2, null=True) price = models.DecimalField(max_digits=6, decimal_places=2, null=True)
def get_price(self): def get_price(self):
""" Compute booking price as a function of place and dates """ """ Compute booking price as a function of place and dates. """
if type(self.start) == str: if isinstance(self.start, str):
self.start = datetime.strptime(self.start, '%Y-%m-%d') self.start = datetime.strptime(self.start, '%Y-%m-%d')
if type(self.end) == str: if isinstance(self.end, str):
self.end = datetime.strptime(self.end, '%Y-%m-%d') self.end = datetime.strptime(self.end, '%Y-%m-%d')
nights = (self.end - self.start).days nights = (self.end - self.start).days
return self.place.price * nights return self.place.price * nights
def send_quotation(self): def send_quotation(self):
""" Notify user of booking details """
mailer.send_quotation.delay(self.guest.name, self.guest.email) mailer.send_quotation.delay(self.guest.name, self.guest.email)

View file

@ -1,9 +1,12 @@
from django.db import models from django.db import models
import rental.tasks.apiMailer as mailer # or gMailer import rental.tasks.api_mailer as mailer # or g_mailer
class Contact(models.Model): class Contact(models.Model):
"""
Contact encapsulates communication with a customer.
"""
def __str__(self): def __str__(self):
return f"Message de {self.name}, le {self.date}" return f"Message de {self.name}, le {self.date}"
@ -15,9 +18,13 @@ class Contact(models.Model):
date = models.DateTimeField(auto_now_add=True) date = models.DateTimeField(auto_now_add=True)
def send_confirmation(self): def send_confirmation(self):
""" Notify that the message has been succesfully sent. """
mailer.send_confirmation.delay(self.name, self.email) mailer.send_confirmation.delay(self.name, self.email)
def send_notification(self): def send_notification(self):
""" Notify admins that a message has been sent. """
mailer.send_notification.delay( mailer.send_notification.delay(
self.name, self.name,
self.email, self.email,

View file

@ -2,6 +2,7 @@ from django.db import models
class Guest(models.Model): class Guest(models.Model):
""" Guest summarizes customer information. """
class Meta: class Meta:
verbose_name = "Voyageur" verbose_name = "Voyageur"

View file

@ -2,6 +2,8 @@ from django.db import models
class Picture(models.Model): class Picture(models.Model):
""" Picture manages media and set alt tags. """
def __str__(self): def __str__(self):
return self.alt return self.alt

View file

@ -5,6 +5,8 @@ from rental.models.picture import Picture
class Place(models.Model): class Place(models.Model):
"""Place encapsulates a loaction information."""
class Meta: class Meta:
verbose_name = "Appartement" verbose_name = "Appartement"
@ -21,9 +23,12 @@ class Place(models.Model):
beds = models.IntegerField(null=True, blank=True) beds = models.IntegerField(null=True, blank=True)
max_occupation = models.IntegerField(null=True, blank=True) max_occupation = models.IntegerField(null=True, blank=True)
thumbnail = models.ForeignKey( thumbnail = models.ForeignKey(
Picture, on_delete=models.CASCADE, blank=True, null=True) Picture, on_delete=models.CASCADE, blank=True, null=True
)
images = models.ManyToManyField(Picture, related_name="places", blank=True) images = models.ManyToManyField(Picture, related_name="places", blank=True)
calendar = models.CharField(max_length=350, blank=True, null=True) calendar = models.CharField(max_length=350, blank=True, null=True)
def is_available(self, start, end): def is_available(self, start, end):
"""Check weither a location is available at a given time period."""
return calendar.check_availability(self, start, end) return calendar.check_availability(self, start, end)

View file

@ -5,6 +5,8 @@ from rental.models.guest import Guest
class Testimonial(models.Model): class Testimonial(models.Model):
"""Testimonial represent a past customer testimony."""
class Meta: class Meta:
verbose_name = "Témoignage" verbose_name = "Témoignage"
@ -14,9 +16,12 @@ class Testimonial(models.Model):
author = models.CharField(max_length=200) author = models.CharField(max_length=200)
text = models.TextField(max_length=1000) text = models.TextField(max_length=1000)
picture = models.ImageField( picture = models.ImageField(
max_length=200, upload_to='img/', null=True, blank=True) max_length=200, upload_to='img/', null=True, blank=True
)
link = models.URLField(max_length=200, null=True, blank=True) link = models.URLField(max_length=200, null=True, blank=True)
guest = models.OneToOneField( guest = models.OneToOneField(
Guest, on_delete=models.CASCADE, blank=True, null=True) Guest, on_delete=models.CASCADE, blank=True, null=True
)
reservation = models.OneToOneField( reservation = models.OneToOneField(
Booking, on_delete=models.CASCADE, blank=True, null=True) Booking, on_delete=models.CASCADE, blank=True, null=True
)

View file

@ -1,12 +1,3 @@
from __future__ import absolute_import, unicode_literals
from datetime import datetime
import requests
from celery import shared_task
from villafleurie.settings import DEFAULT_FROM_EMAIL, EMAIL_HOST_USER
""" Mailer Service used to send messages using API WebHooks. """ Mailer Service used to send messages using API WebHooks.
All Mailers must implement the following methods: All Mailers must implement the following methods:
@ -17,7 +8,18 @@ from villafleurie.settings import DEFAULT_FROM_EMAIL, EMAIL_HOST_USER
def send_quotation(name, email)->void def send_quotation(name, email)->void
""" """
URL = "https://hooks.zapier.com/hooks/catch/4071838/o93celz/" from __future__ import absolute_import, unicode_literals
from datetime import datetime
import os
import requests
from celery import shared_task
from villafleurie.settings import DEFAULT_FROM_EMAIL, EMAIL_HOST_USER
# get mailing hook URL from env
URL = os.environ.get("MAIL_HOOK")
@shared_task @shared_task
@ -32,8 +34,7 @@ def send_confirmation(name, email):
"body": f"Merci {name}, nous avons bien reçu votre message, nous revenons vers vous rapidement !\nCordialement,\nNilka (VillaFleurie)" "body": f"Merci {name}, nous avons bien reçu votre message, nous revenons vers vous rapidement !\nCordialement,\nNilka (VillaFleurie)"
} }
resp = requests.post(URL, data=payload) requests.post(URL, data=payload)
@shared_task @shared_task
@ -48,12 +49,12 @@ def send_notification(name, email, subject, message, date):
"body": f"Sujet : {subject}\nDe : {name}, {email}\nLe : {date}\nMessage : {message}\nCordialement,\nNilka (VillaFleurie)" "body": f"Sujet : {subject}\nDe : {name}, {email}\nLe : {date}\nMessage : {message}\nCordialement,\nNilka (VillaFleurie)"
} }
resp = requests.post(URL, data=payload) requests.post(URL, data=payload)
@shared_task @shared_task
def send_quotation(name, email): def send_quotation(name, email):
""" Send quotation to customer """ """ Notify admins then send quotation to customer """
send_notification( send_notification(
name, name,

View file

@ -1,11 +1,3 @@
from __future__ import absolute_import, unicode_literals
from celery import shared_task
from django.core.mail import mail_admins, send_mail
from villafleurie.settings import EMAIL_HOST_USER
""" Mailer Service used to send messages using Gmail. """ Mailer Service used to send messages using Gmail.
All Mailers must implement the following methods: All Mailers must implement the following methods:
@ -14,12 +6,20 @@ from villafleurie.settings import EMAIL_HOST_USER
def send_notification(name, email, subject, message)->void def send_notification(name, email, subject, message)->void
def send_quotation(name, email)->void def send_quotation(name, email)->void
""" """
from __future__ import absolute_import, unicode_literals
from celery import shared_task
from django.core.mail import mail_admins, send_mail
from villafleurie.settings import EMAIL_HOST_USER
@shared_task @shared_task
def send_confirmation(name, email): def send_confirmation(name, email):
""" Send confirmation message to customer """ """ Send confirmation message to customer """
subject = "Nous avons reçu votre message" subject = "Nous avons reçu votre message"
message = f" Merci {name}, Bien reçu nous revenons vers vous rapidement !" message = f" Merci {name}, Bien reçu nous revenons vers vous rapidement !"
@ -44,4 +44,5 @@ def send_notification(name, email, subject, message):
@shared_task @shared_task
def send_quotation(name, email): def send_quotation(name, email):
""" Send quotation to customer """ """ Send quotation to customer """
send_confirmation_mail(name, email)
send_confirmation(name, email)

View file

@ -1,3 +1,5 @@
""" URLs specifi to rental application """
from django.contrib.staticfiles.urls import static, staticfiles_urlpatterns from django.contrib.staticfiles.urls import static, staticfiles_urlpatterns
from django.urls import path from django.urls import path
@ -18,7 +20,7 @@ urlpatterns = [
path('', place.index, name='index'), path('', place.index, name='index'),
path('hebergements/', place.all, name='list_place'), path('hebergements/', place.all, name='list_place'),
path('<place_name>/', place.view, name='detail_place') path('<slug:place_name>/', place.view, name='detail_place')
] ]
urlpatterns += staticfiles_urlpatterns() urlpatterns += staticfiles_urlpatterns()

View file

@ -10,12 +10,15 @@ from rental.models.place import Place
def view(request): def view(request):
"""Return initial booking form."""
context, template = handle_booking_form(request) context, template = handle_booking_form(request)
return render(request, template, context) return render(request, template, context)
def handle_booking_form(request, context={}, init_template='rental/reservation.html'): def handle_booking_form(request, context={}, init_template='rental/reservation.html'):
"""Validates form and checks if place availability a given period."""
if request.method == 'POST': if request.method == 'POST':
form = BookingForm(request.POST) form = BookingForm(request.POST)
if form.is_valid(): if form.is_valid():
@ -56,17 +59,18 @@ def handle_booking_form(request, context={}, init_template='rental/reservation.h
} }
template = 'rental/merci.html' template = 'rental/merci.html'
return context, template return context, template
else:
form.add_error(None, ValidationError( form.add_error(None, ValidationError(
_("Cet hébergement n'est pas disponible aux dates indiquées."), _("Cet hébergement n'est pas disponible aux dates indiquées."),
code='invalid' code='invalid'
)) ))
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 = BookingForm() form = BookingForm()

View file

@ -5,6 +5,8 @@ from rental.models.contact import Contact
def view(request): def view(request):
"""Handle contactForm."""
if request.method == 'POST': if request.method == 'POST':
form = ContactForm(request.POST) form = ContactForm(request.POST)
if form.is_valid(): if form.is_valid():

View file

@ -1,9 +1,11 @@
from django.shortcuts import render from django.shortcuts import render
def handler404(request, exception): def handler404(request, _):
"""handle 404 errors. Requires a request and an exception."""
return render(request, 'rental/404.html', status=404) return render(request, 'rental/404.html', status=404)
def handler500(request): def handler500(request):
"""handle 500 errors."""
return render(request, 'rental/500.html', status=500) return render(request, 'rental/500.html', status=500)

View file

@ -1,5 +1,4 @@
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.utils.translation import gettext_lazy as _
from rental.models.place import Place from rental.models.place import Place
from rental.models.testimonial import Testimonial from rental.models.testimonial import Testimonial
@ -7,6 +6,7 @@ from rental.views.booking import handle_booking_form
def index(request): def index(request):
"""Display homePage."""
places = Place.objects.all() places = Place.objects.all()
temoignages = Testimonial.objects.all() temoignages = Testimonial.objects.all()
@ -19,6 +19,7 @@ def index(request):
def all(request): def all(request):
"""Handle places list page."""
places = Place.objects.all() places = Place.objects.all()
context = {'places': places} context = {'places': places}
@ -27,6 +28,7 @@ def all(request):
def view(request, place_name='T2'): def view(request, place_name='T2'):
"""Handle a specific place."""
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()

View file

@ -1,3 +1,7 @@
""" Celery is a distributed task queue used for real-time processing of
asynchronous actions
"""
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
import os import os
@ -12,4 +16,5 @@ app.autodiscover_tasks()
@app.task(bind=True) @app.task(bind=True)
def debug_task(self): def debug_task(self):
""" Print request. For debug purposes only. """
print(f'Request: {self.request!r}') print(f'Request: {self.request!r}')

View file

@ -1,3 +1,5 @@
""" Standard setting file to be imported by specific configurations """
import os import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) BASE_DIR = os.path.dirname(os.path.dirname(__file__))

View file

@ -1,6 +1,8 @@
""" Settings to be used in development """
import os import os
from .base import BASE_DIR from .base import *
SECRET_KEY = "not_so_secret_key" SECRET_KEY = "not_so_secret_key"
DEBUG = True DEBUG = True

View file

@ -1,3 +1,5 @@
""" Settings to be used in production """
import os import os
from .base import * from .base import *

View file

@ -1,10 +1,10 @@
from django.conf import settings from django.conf.urls import include
from django.conf.urls import include, url
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), # change admin path for security purposes
path('dashboard/', admin.site.urls),
path('', include('rental.urls', namespace='rental')) path('', include('rental.urls', namespace='rental'))
] ]