models.py 4.9 KB

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