diff --git a/tailbone/grids/filters.py b/tailbone/grids/filters.py index 0014c750..3a497d13 100644 --- a/tailbone/grids/filters.py +++ b/tailbone/grids/filters.py @@ -553,6 +553,7 @@ class AlchemyDateFilter(AlchemyGridFilter): 'greater_equal': "on or after", 'less_than': "before", 'less_equal': "on or before", + 'between': "between", 'is_null': "is null", 'is_not_null': "is not null", 'is_any': "is any", @@ -562,8 +563,18 @@ class AlchemyDateFilter(AlchemyGridFilter): """ Expose greater-than / less-than verbs in addition to core. """ - return ['equal', 'not_equal', 'greater_than', 'greater_equal', - 'less_than', 'less_equal', 'is_null', 'is_not_null', 'is_any'] + return [ + 'equal', + 'not_equal', + 'greater_than', + 'greater_equal', + 'less_than', + 'less_equal', + 'between', + 'is_null', + 'is_not_null', + 'is_any', + ] def make_date(self, value): """ @@ -577,6 +588,44 @@ class AlchemyDateFilter(AlchemyGridFilter): else: return dt.date() + def filter_between(self, query, value): + """ + Filter data with a "between" query. Really this uses ">=" and "<=" + (inclusive) logic instead of SQL "between" keyword. + """ + if value is None or value == '': + return query + + if '|' not in value: + return query + + values = value.split('|') + if len(values) != 2: + return query + + start_date, end_date = values + if start_date: + start_date = self.make_date(start_date) + if end_date: + end_date = self.make_date(end_date) + + # we'll only filter if we have start and/or end date + if not start_date and not end_date: + return query + + return self.filter_date_range(query, start_date, end_date) + + def filter_date_range(self, query, start_date, end_date): + """ + This method should actually apply filter(s) to the query, according to + the given date range. Subclasses may override this logic. + """ + if start_date: + query = query.filter(self.column >= start_date) + if end_date: + query = query.filter(self.column <= end_date) + return query + class AlchemyDateTimeFilter(AlchemyDateFilter): """ @@ -666,6 +715,17 @@ class AlchemyDateTimeFilter(AlchemyDateFilter): time = make_utc(localtime(self.config, time)) return query.filter(self.column < time) + def filter_date_range(self, query, start_date, end_date): + if start_date: + start_time = datetime.datetime.combine(start_date, datetime.time(0)) + start_time = localtime(self.config, start_time) + query = query.filter(self.column >= make_utc(start_time)) + if end_date: + end_time = datetime.datetime.combine(end_date + datetime.timedelta(days=1), datetime.time(0)) + end_time = localtime(self.config, end_time) + query = query.filter(self.column <= make_utc(end_time)) + return query + class AlchemyLocalDateTimeFilter(AlchemyDateTimeFilter): """ @@ -756,6 +816,17 @@ class AlchemyLocalDateTimeFilter(AlchemyDateTimeFilter): time = localtime(self.config, time, tzinfo=False) return query.filter(self.column < time) + def filter_date_range(self, query, start_date, end_date): + if start_date: + start_time = datetime.datetime.combine(start_date, datetime.time(0)) + start_time = localtime(self.config, start_time, tzinfo=False) + query = query.filter(self.column >= start_time) + if end_date: + end_time = datetime.datetime.combine(end_date + datetime.timedelta(days=1), datetime.time(0)) + end_time = localtime(self.config, end_time, tzinfo=False) + query = query.filter(self.column <= end_time) + return query + class AlchemyGPCFilter(AlchemyGridFilter): """ diff --git a/tailbone/static/js/tailbone.buefy.datepicker.js b/tailbone/static/js/tailbone.buefy.datepicker.js index 5cc620bf..fe649380 100644 --- a/tailbone/static/js/tailbone.buefy.datepicker.js +++ b/tailbone/static/js/tailbone.buefy.datepicker.js @@ -14,6 +14,7 @@ const TailboneDatepicker = { ':value="value ? parseDate(value) : null"', '@input="dateChanged"', ':disabled="disabled"', + 'ref="trueDatePicker"', '>', '' ].join(' '), @@ -49,7 +50,11 @@ const TailboneDatepicker = { dateChanged(date) { this.$emit('input', this.formatDate(date)) - } + }, + + focus() { + this.$refs.trueDatePicker.focus() + }, } } diff --git a/tailbone/static/js/tailbone.buefy.grid.js b/tailbone/static/js/tailbone.buefy.grid.js index ee873c6d..8af8b5b0 100644 --- a/tailbone/static/js/tailbone.buefy.grid.js +++ b/tailbone/static/js/tailbone.buefy.grid.js @@ -1,4 +1,53 @@ +const GridFilterDateValue = { + template: '#grid-filter-date-value-template', + props: { + value: String, + dateRange: Boolean, + }, + data() { + return { + startDate: null, + endDate: null, + } + }, + mounted() { + if (this.dateRange) { + if (this.value.includes('|')) { + let values = this.value.split('|') + if (values.length == 2) { + this.startDate = values[0] + this.endDate = values[1] + } else { + this.startDate = this.value + } + } else { + this.startDate = this.value + } + } else { + this.startDate = this.value + } + }, + methods: { + focus() { + this.$refs.startDate.focus() + }, + startDateChanged(value) { + if (this.dateRange) { + value += '|' + this.endDate + } + this.$emit('input', value) + }, + endDateChanged(value) { + value = this.startDate + '|' + value + this.$emit('input', value) + }, + }, +} + +Vue.component('grid-filter-date-value', GridFilterDateValue) + + const GridFilter = { template: '#grid-filter-template', props: { @@ -14,6 +63,23 @@ const GridFilter = { }) }, + valuedVerb() { + /* this returns true if the filter's current verb should expose value input(s) */ + + // if filter has no "valueless" verbs, then all verbs should expose value inputs + if (!this.filter.valueless_verbs) { + return true + } + + // if filter *does* have valueless verbs, check if "current" verb is valueless + if (this.filter.valueless_verbs.includes(this.filter.verb)) { + return false + } + + // current verb is *not* valueless + return true + }, + focusValue: function() { this.$refs.valueInput.focus() // this.$refs.valueInput.select() diff --git a/tailbone/templates/grids/buefy.mako b/tailbone/templates/grids/buefy.mako index 9fcf5f9b..39feedd5 100644 --- a/tailbone/templates/grids/buefy.mako +++ b/tailbone/templates/grids/buefy.mako @@ -1,5 +1,29 @@ ## -*- coding: utf-8; -*- + +