models.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. from datetime import datetime
  2. from django.conf import settings
  3. from django.core.validators import RegexValidator
  4. from django.db import models
  5. from modelcluster.fields import ParentalKey
  6. from wagtail.wagtailadmin.edit_handlers import (
  7. FieldPanel,
  8. InlinePanel,
  9. StreamFieldPanel)
  10. from wagtail.wagtailcore.models import Orderable, Page
  11. from wagtail.wagtailimages.edit_handlers import (
  12. ImageChooserPanel,
  13. )
  14. from wagtail.wagtailcore.fields import StreamField
  15. from wagtail.wagtailsearch import index
  16. from bakerydemo.base.blocks import BaseStreamBlock
  17. class OperatingHours(models.Model):
  18. """
  19. Django model to capture operating hours for a Location
  20. """
  21. MONDAY = 'Mon'
  22. TUESDAY = 'Tue'
  23. WEDNESDAY = 'Wed'
  24. THURSDAY = 'Thu'
  25. FRIDAY = 'Fri'
  26. SATURDAY = 'Sat'
  27. SUNDAY = 'Sun'
  28. DAY_CHOICES = (
  29. (MONDAY, 'Mon'),
  30. (TUESDAY, 'Tue'),
  31. (WEDNESDAY, 'Weds'),
  32. (THURSDAY, 'Thu'),
  33. (FRIDAY, 'Fri'),
  34. (SATURDAY, 'Sat'),
  35. (SUNDAY, 'Sun'),
  36. )
  37. day = models.CharField(
  38. max_length=4,
  39. choices=DAY_CHOICES,
  40. default=MONDAY,
  41. )
  42. opening_time = models.TimeField(
  43. blank=True,
  44. null=True)
  45. closing_time = models.TimeField(
  46. blank=True,
  47. null=True)
  48. closed = models.BooleanField(
  49. "Closed?",
  50. blank=True,
  51. help_text='Tick if location is closed on this day'
  52. )
  53. panels = [
  54. FieldPanel('day'),
  55. FieldPanel('opening_time'),
  56. FieldPanel('closing_time'),
  57. FieldPanel('closed'),
  58. ]
  59. class Meta:
  60. abstract = True
  61. def __str__(self):
  62. return '{}: {} - {} {}'.format(
  63. self.day,
  64. self.opening_time.strftime('%H:%M'),
  65. self.closing_time.strftime('%H:%M'),
  66. settings.TIME_ZONE
  67. )
  68. class LocationOperatingHours(Orderable, OperatingHours):
  69. """
  70. Operating Hours entry for a Location
  71. """
  72. location = ParentalKey(
  73. 'LocationPage',
  74. related_name='hours_of_operation'
  75. )
  76. class LocationsIndexPage(Page):
  77. """
  78. Index page for locations
  79. """
  80. introduction = models.TextField(
  81. help_text='Text to describe the index page',
  82. blank=True)
  83. image = models.ForeignKey(
  84. 'wagtailimages.Image',
  85. null=True,
  86. blank=True,
  87. on_delete=models.SET_NULL,
  88. related_name='+',
  89. help_text='Location listing image'
  90. )
  91. subpage_types = ['LocationPage']
  92. content_panels = Page.content_panels + [
  93. FieldPanel('introduction'),
  94. ImageChooserPanel('image'),
  95. ]
  96. def get_context(self, request):
  97. context = super(LocationsIndexPage, self).get_context(request)
  98. context['locations'] = LocationPage.objects.descendant_of(
  99. self).live().order_by(
  100. 'title')
  101. return context
  102. class LocationPage(Page):
  103. """
  104. Detail for a specific bakery location.
  105. """
  106. introduction = models.TextField(
  107. help_text='Text to describe the index page',
  108. blank=True)
  109. address = models.TextField()
  110. image = models.ForeignKey(
  111. 'wagtailimages.Image',
  112. null=True,
  113. blank=True,
  114. on_delete=models.SET_NULL,
  115. related_name='+'
  116. )
  117. lat_long = models.CharField(
  118. max_length=36,
  119. help_text="Comma separated lat/long. (Ex. 64.144367, -21.939182) \
  120. Right click Google Maps and select 'What\'s Here'",
  121. validators=[
  122. RegexValidator(
  123. regex='^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$',
  124. message='Lat Long must be a comma-separated numeric lat and long',
  125. code='invalid_lat_long'
  126. ),
  127. ]
  128. )
  129. body = StreamField(
  130. BaseStreamBlock(), verbose_name="About this location", blank=True
  131. )
  132. # We've defined the StreamBlock() within blocks.py that we've imported on
  133. # line 12. Defining it in a different file gives us consistency across the
  134. # site, though StreamFields _can_ be created on a per model basis if you
  135. # have a use case for it
  136. # Search index configuration
  137. search_fields = Page.search_fields + [
  138. index.SearchField('address'),
  139. ]
  140. # Editor panels configuration
  141. content_panels = Page.content_panels + [
  142. FieldPanel('introduction', classname="full"),
  143. StreamFieldPanel('body'),
  144. FieldPanel('address', classname="full"),
  145. FieldPanel('lat_long'),
  146. ImageChooserPanel('image'),
  147. InlinePanel('hours_of_operation', label="Hours of Operation"),
  148. ]
  149. def __str__(self):
  150. return self.title
  151. @property
  152. def operating_hours(self):
  153. hours = self.hours_of_operation.all()
  154. return hours
  155. def is_open(self):
  156. # Determines if the location is currently open
  157. now = datetime.now()
  158. current_time = now.time()
  159. current_day = now.strftime('%a').upper()
  160. try:
  161. self.operating_hours.get(
  162. day=current_day,
  163. opening_time__lte=current_time,
  164. closing_time__gte=current_time
  165. )
  166. return True
  167. except LocationOperatingHours.DoesNotExist:
  168. return False
  169. def get_context(self, request):
  170. context = super(LocationPage, self).get_context(request)
  171. context['lat'] = self.lat_long.split(",")[0]
  172. context['long'] = self.lat_long.split(",")[1]
  173. return context
  174. parent_page_types = ['LocationsIndexPage']