From dfc0e420830b73fcf9ec13c25f8f6746cab2fbb0 Mon Sep 17 00:00:00 2001 From: Eric O'Callaghan Date: Sat, 4 Jan 2025 01:27:08 -0500 Subject: [PATCH] date picker --- apps/core/api/viewsets.py | 23 +++++++++++++- apps/core/forms.py | 33 ++++++++++++++++++++ apps/core/templates/base.html | 11 +++++-- apps/core/templates/utility.html | 2 +- apps/core/urls.py | 4 ++- apps/core/views/base.py | 53 +++++++++++++++++++++++++++++--- apps/core/views/utility.py | 1 - apps/electric/views.py | 2 +- apps/natural_gas/views.py | 1 - apps/water/views.py | 1 - 10 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 apps/core/forms.py diff --git a/apps/core/api/viewsets.py b/apps/core/api/viewsets.py index f0ca9a6..4ea593c 100644 --- a/apps/core/api/viewsets.py +++ b/apps/core/api/viewsets.py @@ -1,7 +1,8 @@ +from datetime import date from dateutil.relativedelta import relativedelta from django.utils.decorators import method_decorator -from django.utils.timezone import localtime +from django.utils.timezone import localdate, localtime from django.views.decorators.cache import cache_page from rest_framework.exceptions import ParseError @@ -22,6 +23,26 @@ def dispatch(self, request, *args, **kwargs): def get_queryset(self): qs = super().get_queryset() + start_date = None + start_search = self.request.query_params.get("start") + if start_search: + try: + start_date = date.fromisoformat(start_search) + except ValueError: + pass + + end_date = None + end_search = self.request.query_params.get("end") + if end_search: + try: + end_date = date.fromisoformat(end_search) + except ValueError: + pass + + if start_date and end_date: + return qs.filter(pk__range=[start_date, end_date]) + + if self.filterable: for filterable in self.filterable: value = self.request.query_params.get(filterable) diff --git a/apps/core/forms.py b/apps/core/forms.py new file mode 100644 index 0000000..cd294a7 --- /dev/null +++ b/apps/core/forms.py @@ -0,0 +1,33 @@ +from django import forms +from django.utils.timezone import localtime + + +class SearchDateForm(forms.Form): + """Form to search for data, between start and end dates.""" + start = forms.DateField( + required=False, + widget=forms.DateInput( + attrs={ + 'class': 'form-control-sm', + 'label': 'start', + 'name': 'start', + 'type': 'date' + } + ) + ) + end = forms.DateField( + required=False, + widget=forms.DateInput( + attrs={ + 'class': 'form-control-sm', + 'label': 'end', + 'name': 'end', + 'type': 'date' + } + ) + ) + + def get_initial_for_field(self, field, field_name): + # Set date inputs "max" attribute to current time. + field.widget.attrs["max"] = localtime().strftime("%Y-%m-%d") + return super().get_initial_for_field(field, field_name) diff --git a/apps/core/templates/base.html b/apps/core/templates/base.html index 7ccc7c7..e95d69d 100644 --- a/apps/core/templates/base.html +++ b/apps/core/templates/base.html @@ -54,9 +54,9 @@ -
+
diff --git a/apps/core/templates/utility.html b/apps/core/templates/utility.html index 2434f85..f3cf5dd 100644 --- a/apps/core/templates/utility.html +++ b/apps/core/templates/utility.html @@ -34,7 +34,7 @@ const chartLoading = document.getElementById('chart-loading') const utilityTitle = '{{ title }}' const xhr = new XMLHttpRequest() - xhr.open("GET", `/api/${utilityTitle.replace(" ", "_").toLowerCase()}/?format=json`) + xhr.open("GET", `/api/${utilityTitle.replace(" ", "_").toLowerCase()}/?format=json&start={{ start }}&end={{ end }}`) xhr.onreadystatechange = async () => { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { const utilityUsageData = JSON.parse(xhr.responseText) diff --git a/apps/core/urls.py b/apps/core/urls.py index 3bd3a28..1724a72 100644 --- a/apps/core/urls.py +++ b/apps/core/urls.py @@ -3,4 +3,6 @@ from .views.home import HomeView -urlpatterns = [path("", HomeView.as_view(), name="home")] +urlpatterns = [ + path("", HomeView.as_view(), name="home") +] diff --git a/apps/core/views/base.py b/apps/core/views/base.py index cb79924..7ca67b0 100644 --- a/apps/core/views/base.py +++ b/apps/core/views/base.py @@ -1,17 +1,62 @@ +from datetime import timedelta + from django.conf import settings -from django.views.generic.base import TemplateView +from django.utils.timezone import localdate +from django.views.generic.edit import FormView + +from ..forms import SearchDateForm -class BaseView(TemplateView): +class BaseView(FormView): """Base view.""" - http_method_names = ("get",) - # icon = None + dates = initial = {} + default_start = {} + form_class = SearchDateForm + http_method_names = ("get", "post") template_name = "utility.html" title = None + def setup(self, request, *args, **kwargs): + # Set default search start and end datetime values. + + today = localdate() + self.dates["start"] = None + if self.default_start: + self.dates["start"] = today - timedelta(**self.default_start) + self.dates["end"] = today + + return super().setup(request, *args, **kwargs) + + + def form_valid(self, form): + # When valid, use the submitted form search dates. + for when in "start", "end": + if form.cleaned_data[when]: + self.dates[when] = form.cleaned_data[when] + + return self.render_to_response(self.get_context_data(form=form)) + def get_context_data(self, **kwargs): + # Include start and end dates in context. context = super().get_context_data(**kwargs) + + for when in "start", "end": + if self.dates[when]: + context[when] = self.dates[when].strftime("%Y-%m-%d") + context["timezone"] = settings.TIME_ZONE context["title"] = self.title.replace("_", " ").title() context["website_title"] = settings.WEBSITE_TITLE + return context + + def get_initial(self): + # Set initial form search date values. + initial = super().get_initial() + for when in "start", "end": + if self.dates[when]: + initial[when] = self.dates[when].strftime("%Y-%m-%d") + return initial + + def get_success_url(self): + return self.request.path diff --git a/apps/core/views/utility.py b/apps/core/views/utility.py index d98106a..78cab4a 100644 --- a/apps/core/views/utility.py +++ b/apps/core/views/utility.py @@ -18,6 +18,5 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["color"] = self.color context["datatable_time_format"] = self.datatable_time_format - # context["icon"] = self.icon context["thresholds"] = self.thresholds return context diff --git a/apps/electric/views.py b/apps/electric/views.py index 69f313b..cf782da 100644 --- a/apps/electric/views.py +++ b/apps/electric/views.py @@ -5,5 +5,5 @@ class ElectricView(UtilityView): """Electric usage view.""" color = "e4a11b" datatable_time_format = "datetime('DDDD, tt')" - # icon = "lightbulb" + default_start = {"days": 366.5} thresholds = (1, 0.75, 0.5) diff --git a/apps/natural_gas/views.py b/apps/natural_gas/views.py index eb13d90..c9779b4 100644 --- a/apps/natural_gas/views.py +++ b/apps/natural_gas/views.py @@ -5,5 +5,4 @@ class NaturalGasView(UtilityView): """Natural gas usage view.""" color = "853cfd" datatable_time_format = "datetime('MMMM y')" - # icon = "fan" thresholds = (50, 25, 10) diff --git a/apps/water/views.py b/apps/water/views.py index f48cc47..561cc8d 100644 --- a/apps/water/views.py +++ b/apps/water/views.py @@ -5,5 +5,4 @@ class WaterView(UtilityView): """Water usage view.""" color = "2caffe" datatable_time_format = "date('DDDD')" - # icon = "droplet" thresholds = (100, 75, 50)