Permalink
Browse files

[Bug 793847] Implement date filtering.

This includes a jquery-ui datepicker for selecting dates.
  • Loading branch information...
1 parent b47cfd2 commit f80344516afbb564edc56776143d90541b025209 @mythmon committed Sep 24, 2012
@@ -1,4 +1,36 @@
$(function() {
+ // General widgets.
+ // Expandos
+ $('.expando').hide();
+ $('.expander').bind('click', function() {
+ var $this = $(this);
+ var $for = $('#' + $this.attr('for'));
+ $for.fadeToggle();
+ $this.toggleClass('selected');
+ });
+
+ // Datepickers.
+ $('input[type=date]').datepicker({
+ dateFormat: 'yy-mm-dd',
+ });
+
+
+ // Set up the when selector.
+ var $date_start = $('#whensubmit').siblings('input[name=date_start]');
+ var $date_end = $('#whensubmit').siblings('input[name=date_end]');
+ if ($date_start.val() || $date_end.val()) {
+ $('#whentext').show();
+ $('.expander[for=whentext]').addClass('selected');
+ }
+
+ $('#whensubmit').bind('click', function() {
+ setQuerystring(getQuerystring(), {
+ date_start: $date_start.val(),
+ date_end: $date_end.val()
+ });
+ });
+
+
// Draw bars in the left column.
$('ul.bars').each(function() {
var $ul = $(this);
@@ -43,6 +75,7 @@ $(function() {
});
});
+
// Draw histogram in the middle column.
var $histogram = $('.graph .histogram');
var data = $histogram.data('histogram');
@@ -19,7 +19,9 @@
{{ feedback.description }}
</p>
<ul class="meta">
- <li><time datetime="{{ feedback.created }}Z" title="{{ feedback.created }} UTC">{{ feedback.created|naturaltime }}</time></li>
+ <li><time datetime="{{ feedback.created }}Z" title="{{ feedback.created }} UTC">
+ {{ feedback.created|naturaltime }}
+ </time></li>
<li>{{ feedback.platform }}</li>
<li>{{ feedback.locale|locale_name }}</li>
</ul>
@@ -42,6 +44,24 @@
</div>
{% endmacro %}
+{% macro date_range_button(days, text) %}
+ {% set days_text = '%sd' % days %}
+ {% if selected == days_text %}
+ {% set sel = 'selected' %}
+ {% endif %}
+
+ {% if days is none %}
+ {% set ago = None %}
+ {% else %}
+ {% set ago = date_ago(days=days) %}
+ {% endif %}
+
+ {# date_end=None here is to remove the date_end parameter from the query string. #}
+ {% set href = request.get_full_path()|urlparams(date_start=ago, date_end=None, selected=days_text) %}
+
+ <a class="button when {{ sel }}" href="{{ href }}">{{ text }}</a>
+{% endmacro %}
+
{% block body_id %}dashboard{% endblock %}
{% block content %}
@@ -50,6 +70,29 @@
{% block content_leftside %}
<div class="block search">
+ <div class="filter">
+ <h3>When</h3>
+ {# L10n: Short for 1 day. #}
+ {{ date_range_button(1, _('1d')) }}
+ {# L10n: Short for 7 days. #}
+ {{ date_range_button(7, _('7d')) }}
+ {# L10n: Short for 30 days. #}
+ {{ date_range_button(30, _('30d')) }}
+ {# The `None` here is to remove the start_date from the URL. #}
+ {# L10n: Symbol for "all" date range. #}
+ {{ date_range_button(None, _('&infin;')) }}
+
+ <a href="#" class="expander button" for="whentext">{{ _('&raquo;') }}</a>
+
+ <div class="expando" id="whentext">
+ <input type="date" name="date_start" value="{{ current_search.date_start }}"
+ placeholder="{{ _('Start') }}"> -
+ <input type="date" name="date_end" value="{{ current_search.date_end }}"
+ placeholder="{{ _('End') }}">
+ <a href="#" class="button" id="whensubmit">{{ _('Set') }}</a>
+ </div>
+ </div>
+
{% for filter_set in filter_data %}
{{ filter_block(filter_set) }}
{% endfor %}
@@ -1,3 +1,4 @@
+from datetime import datetime
import logging
from nose.tools import eq_
@@ -82,18 +83,18 @@ def setUp(self):
# 4 happy, 3 sad.
# 2 Windows XP, 2 Linux, 1 OS X, 2 Windows 7
items = [
- (True, 'Windows XP', 'en-US', 'apple'),
- (True, 'Windows 7', 'es', 'banana'),
- (True, 'Linux', 'en-US', 'orange'),
- (True, 'Linux', 'en-US', 'apple'),
- (False, 'Windows XP', 'en-US', 'banana'),
- (False, 'Windows 7', 'en-US', 'orange'),
- (False, 'Linux', 'es', 'apple'),
+ (True, 'Windows XP', 'en-US', 'apple', datetime(2012, 1, 1)),
+ (True, 'Windows 7', 'es', 'banana', datetime(2012, 1, 2)),
+ (True, 'Linux', 'en-US', 'orange', datetime(2012, 1, 3)),
+ (True, 'Linux', 'en-US', 'apple', datetime(2012, 1, 4)),
+ (False, 'Windows XP', 'en-US', 'banana', datetime(2012, 1, 5)),
+ (False, 'Windows 7', 'en-US', 'orange', datetime(2012, 1, 6)),
+ (False, 'Linux', 'es', 'apple', datetime(2012, 1, 7)),
]
- for happy, platform, locale, description in items:
+ for happy, platform, locale, description, created in items:
# We don't need to keep this around, just need to create it.
simple(happy=happy, platform=platform, locale=locale,
- description=description, save=True)
+ description=description, created=created, save=True)
self.refresh()
@@ -142,6 +143,24 @@ def test_text_search(self):
pq = PyQuery(r.content)
eq_(len(pq('li.opinion')), 2)
+ def test_date_search(self):
+ url = reverse('dashboard')
+ # Unspecified start => (-infin, end]
+ r = self.client.get(url, {'date_end': '2012-01-05'})
+ pq = PyQuery(r.content)
+ eq_(len(pq('li.opinion')), 5)
+ # Unspecified end => [start, +infin)
+ r = self.client.get(url, {'date_start': '2012-01-04'})
+ pq = PyQuery(r.content)
+ eq_(len(pq('li.opinion')), 4)
+ # Both start and end => [start, end]
+ r = self.client.get(url, {
+ 'date_start': '2012-01-02',
+ 'date_end': '2012-01-06'
+ })
+ pq = PyQuery(r.content)
+ eq_(len(pq('li.opinion')), 5)
+
def test_invalid_search(self):
url = reverse('dashboard')
# Invalid values for happy shouldn't filter
@@ -1,3 +1,5 @@
+from datetime import datetime, timedelta
+
from django.shortcuts import render
from django.template.defaultfilters import slugify
@@ -83,6 +85,10 @@ def dashboard(request, template):
search_platform = request.GET.get('platform', None)
search_locale = request.GET.get('locale', None)
search_query = request.GET.get('q', None)
+ search_date_start = request.GET.get('date_start', None)
+ search_date_end = request.GET.get('date_end', None)
+ selected = request.GET.get('selected', None)
+
current_search = {'page': page}
search = SimpleIndex.search()
@@ -98,6 +104,29 @@ def dashboard(request, template):
if search_locale:
f &= F(locale=search_locale)
current_search['locale'] = search_locale
+
+ if search_date_start:
+ try:
+ start = datetime.strptime(search_date_start, '%Y-%m-%d')
+ # This isn't a mistake. The line below *should* say `search_date_start`
+ current_search['date_start'] = search_date_start
+ f &= F(created__gte=start)
+ except ValueError:
+ # Should something happen here?
+ pass
+ if search_date_end:
+ try:
+ end = datetime.strptime(search_date_end, '%Y-%m-%d')
+ # Add one day, so that the search range includes the entire day.
+ end += timedelta(days=1)
+ # This isn't a mistake. The line below *should* say `search_date_end`
+ current_search['date_end'] = search_date_end
+ # Note 'less than', not 'less than or equal', because of the above.
+ f &= F(created__lt=end)
+ except ValueError:
+ # Should something happen here?
+ pass
+
if search_query:
fields = ['text', 'text_phrase', 'fuzzy']
query = dict(('description__%s' % f, search_query) for f in fields)
@@ -179,4 +208,5 @@ def dashboard(request, template):
'prev_page': page - 1 if start > 0 else None,
'next_page': page + 1 if end < search_count else None,
'current_search': current_search,
+ 'selected': selected,
})
@@ -1,3 +1,4 @@
+from datetime import datetime, timedelta
import time
import json
@@ -41,3 +42,10 @@ def locale_name(locale, native=False, default=_lazy('Unknown')):
return product_details.languages[locale][display_locale]
else:
return default
+
+
+@register.function
+def date_ago(days=0):
+ now = datetime.now()
+ diff = timedelta(days=days)
+ return (now - diff).date()
@@ -29,7 +29,7 @@
// General Styling
html {
- background: #fff;
+ background-color: #fff;
color: #4B4742;
font: 13px Helvetica, Arial, sans-serif;
line-height: 1;
@@ -414,3 +414,37 @@ footer {
font-size: 85%;
}
}
+
+// UI
+
+a.button {
+ background-color: #F0F0F0;
+ padding: 3px 4px;
+ margin-right: 5px;
+ color: #000000;
+ box-shadow: 1px 1px 0px rgba(0, 0, 50, 0.2);
+ position: relative;
+ line-height: 30px;
+
+ &.selected {
+ background-color: #4497C7;
+ }
+
+ &:hover {
+ background-color: #C1DCEC;
+ text-decoration: none;
+ }
+
+ &:active {
+ box-shadow: 0 0 0.5px rgba(0, 0, 50, 0.2);
+ top: 0.5px;
+ left: 0.5px;
+ }
+}
+
+#whentext {
+ margin-top: 5px;
+ input[type=date] {
+ width: 75px;
+ }
+}
@@ -1,3 +1,5 @@
+from datetime import datetime
+
from django.db import models
from elasticutils.contrib.django.models import Indexable
@@ -29,7 +31,7 @@ class Simple(ModelBase):
platform = models.CharField(max_length=30, blank=True)
locale = models.CharField(max_length=8, blank=True)
- created = models.DateTimeField(auto_now_add=True)
+ created = models.DateTimeField(default=datetime.now)
class Meta:
ordering = ['-created']
@@ -64,6 +64,7 @@
'css/feedback.less',
),
'dashboard': (
+ 'css/ui-lightness/jquery-ui.css',
'css/lib/normalize.css',
'css/fjord.less',
'css/dashboard.less',
@@ -84,6 +85,7 @@
),
'dashboard': (
'js/lib/jquery.min.js',
+ 'js/lib/jquery-ui.min.js',
'js/lib/excanvas.js',
'js/lib/jquery.flot.js',
'js/lib/jquery.flot.time.js',

0 comments on commit f803445

Please sign in to comment.