2
0

models.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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.wagtailcore.fields import StreamField
  7. from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel, StreamFieldPanel
  8. from wagtail.wagtailcore.models import Orderable, Page
  9. from wagtail.wagtailsearch import index
  10. from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
  11. from bakerydemo.base.blocks import BaseStreamBlock
  12. from bakerydemo.locations.choices import DAY_CHOICES
  13. class OperatingHours(models.Model):
  14. """
  15. Django model to capture operating hours for a Location
  16. """
  17. day = models.CharField(
  18. max_length=4,
  19. choices=DAY_CHOICES,
  20. default='MON'
  21. )
  22. opening_time = models.TimeField(
  23. blank=True,
  24. null=True)
  25. closing_time = models.TimeField(
  26. blank=True,
  27. null=True)
  28. closed = models.BooleanField(
  29. "Closed?",
  30. blank=True,
  31. help_text='Tick if location is closed on this day'
  32. )
  33. panels = [
  34. FieldPanel('day'),
  35. FieldPanel('opening_time'),
  36. FieldPanel('closing_time'),
  37. FieldPanel('closed'),
  38. ]
  39. class Meta:
  40. abstract = True
  41. def __str__(self):
  42. if self.opening_time:
  43. opening = self.opening_time.strftime('%H:%M')
  44. else:
  45. opening = '--'
  46. if self.closing_time:
  47. closed = self.opening_time.strftime('%H:%M')
  48. else:
  49. closed = '--'
  50. return '{}: {} - {} {}'.format(
  51. self.day,
  52. opening,
  53. closed,
  54. settings.TIME_ZONE
  55. )
  56. class LocationOperatingHours(Orderable, OperatingHours):
  57. """
  58. Operating Hours entry for a Location
  59. """
  60. location = ParentalKey(
  61. 'LocationPage',
  62. related_name='hours_of_operation'
  63. )
  64. class LocationsIndexPage(Page):
  65. """
  66. Index page for locations
  67. """
  68. introduction = models.TextField(
  69. help_text='Text to describe the page',
  70. blank=True)
  71. image = models.ForeignKey(
  72. 'wagtailimages.Image',
  73. null=True,
  74. blank=True,
  75. on_delete=models.SET_NULL,
  76. related_name='+',
  77. help_text='Landscape mode only; horizontal width between 1000px and 3000px.'
  78. )
  79. subpage_types = ['LocationPage']
  80. def children(self):
  81. return self.get_children().specific().live()
  82. def get_context(self, request):
  83. context = super(LocationsIndexPage, self).get_context(request)
  84. context['locations'] = LocationPage.objects.descendant_of(
  85. self).live().order_by(
  86. 'title')
  87. return context
  88. content_panels = Page.content_panels + [
  89. FieldPanel('introduction', classname="full"),
  90. ImageChooserPanel('image'),
  91. ]
  92. class LocationPage(Page):
  93. """
  94. Detail for a specific bakery location.
  95. """
  96. introduction = models.TextField(
  97. help_text='Text to describe the page',
  98. blank=True)
  99. image = models.ForeignKey(
  100. 'wagtailimages.Image',
  101. null=True,
  102. blank=True,
  103. on_delete=models.SET_NULL,
  104. related_name='+',
  105. help_text='Landscape mode only; horizontal width between 1000px and 3000px.'
  106. )
  107. body = StreamField(
  108. BaseStreamBlock(), verbose_name="Page body", blank=True
  109. )
  110. address = models.TextField()
  111. lat_long = models.CharField(
  112. max_length=36,
  113. help_text="Comma separated lat/long. (Ex. 64.144367, -21.939182) \
  114. Right click Google Maps and select 'What\'s Here'",
  115. validators=[
  116. RegexValidator(
  117. regex='^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$',
  118. message='Lat Long must be a comma-separated numeric lat and long',
  119. code='invalid_lat_long'
  120. ),
  121. ]
  122. )
  123. # Search index configuration
  124. search_fields = Page.search_fields + [
  125. index.SearchField('address'),
  126. index.SearchField('body'),
  127. ]
  128. # Editor panels configuration
  129. content_panels = [
  130. FieldPanel('title', classname="full"),
  131. FieldPanel('introduction', classname="full"),
  132. ImageChooserPanel('image'),
  133. StreamFieldPanel('body'),
  134. FieldPanel('address', classname="full"),
  135. FieldPanel('lat_long'),
  136. InlinePanel('hours_of_operation', label="Hours of Operation"),
  137. ]
  138. def __str__(self):
  139. return self.title
  140. @property
  141. def operating_hours(self):
  142. hours = self.hours_of_operation.all()
  143. return hours
  144. def is_open(self):
  145. # Determines if the location is currently open
  146. now = datetime.now()
  147. current_time = now.time()
  148. current_day = now.strftime('%a').upper()
  149. try:
  150. self.operating_hours.get(
  151. day=current_day,
  152. opening_time__lte=current_time,
  153. closing_time__gte=current_time
  154. )
  155. return True
  156. except LocationOperatingHours.DoesNotExist:
  157. return False
  158. def get_context(self, request):
  159. context = super(LocationPage, self).get_context(request)
  160. context['lat'] = self.lat_long.split(",")[0]
  161. context['long'] = self.lat_long.split(",")[1]
  162. context['google_map_api_key'] = settings.GOOGLE_MAP_API_KEY
  163. return context
  164. parent_page_types = ['LocationsIndexPage']