Commits

Luke Plant committed fb4a231

Implemented month range lookup

Comments (0)

Files changed (3)

django_easyfilters/filters.py

             if self.range_type == YEAR:
                 return value
             elif self.range_type == MONTH:
-                month = date(int(parts[0]), int(parts[1]), 1)
-                return capfirst(formats.date_format(month, 'YEAR_MONTH_FORMAT'))
+                from django.utils.dates import MONTHS
+                return unicode(MONTHS[int(parts[1])])
             elif self.range_type == DAY:
                 return str(int(parts[-1]))
         else:
                 return DateChoice(drt, list(m.groups()))
 
     def make_lookup(self, field_name):
+        # It's easier to do this all using datetime comparisons, than have a
+        # separate path for the single year/month/day case.
         if self.range_type.single:
-            val = self.values[0]
-            # val can contain:
-            # yyyy
-            # yyyy-mm
-            # yyyy-mm-dd
-            # Need to look up last part, converted to int
-            parts = val.split('-')
-            return {field_name + '__' + self.range_type.label : int(parts[-1])}
+            start, end = self.values[0], self.values[0]
         else:
-            # Should be just two values. First is lower bound, second is upper
-            # bound. Need to convert to datetime objects.
-            start_parts = map(int, self.values[0].split('-'))
-            end_parts = map(int, self.values[1].split('-'))
-            if self.range_type == YEARGROUP:
-                return {field_name + '__gte': date(start_parts[0], 1, 1),
-                        field_name + '__lt': date(end_parts[0] + 1, 1, 1)}
-            else:
-                return {}
+            start, end = self.values
+
+        start_parts = map(int, start.split('-'))
+        end_parts = map(int, end.split('-'))
+        if self.range_type.label == 'year':
+            return {field_name + '__gte': date(start_parts[0], 1, 1),
+                    field_name + '__lt': date(end_parts[0] + 1, 1, 1)}
+        elif self.range_type.label == 'month':
+            yearadd, nextmonth = divmod(end_parts[1] + 1, 12)
+            return {field_name + '__gte': date(start_parts[0], start_parts[1], 1),
+                    field_name + '__lt': date(end_parts[0] + yearadd, nextmonth, 1) }
+        else:
+            return {}
 
 
 class DateTimeFilter(MultiValueFilterMixin, DrillDownMixin, Filter):

django_easyfilters/tests/filterset.py

         with self.assertNumQueries(1):
             choices = f.get_choices(qs_filtered)
 
+        # There are at least 2 books in 1818, in different months.
         self.assertTrue(len([c for c in choices if c.link_type == FILTER_ADD]) >= 2)
         self.assertEqual(len([c for c in choices if c.link_type == FILTER_REMOVE]), 1)
 
         with self.assertNumQueries(1):
             choices = f.get_choices(qs_filtered)
 
+        # There are at least 2 books in 1813..1814, on different years
         self.assertEqual(len([c for c in choices if c.link_type == FILTER_REMOVE]), 1)
         self.assertEqual(len([c for c in choices if c.link_type == FILTER_ADD]), 2)
         self.assertEqual([c.label for c in choices if c.link_type == FILTER_ADD],
                          ['1813', '1814'])
 
+    def test_datetime_filter_single_month_selected(self):
+        params = MultiValueDict({'date_published':['1847-10']})
+        f = DateTimeFilter('date_published', Book, params, max_links=10)
+        qs = Book.objects.all()
+
+        # Should get a number of books in queryset.
+        qs_filtered = f.apply_filter(qs)
+
+        self.assertEqual(list(qs_filtered),
+                         list(qs.filter(date_published__year=1847,
+                                        date_published__month=10)))
+
+        # We only need 1 query if we've already told it what month to look at.
+        with self.assertNumQueries(1):
+            choices = f.get_choices(qs_filtered)
+
+        # There are at least 2 books for Oct 1847, on different days
+        self.assertTrue(len([c for c in choices if c.link_type == FILTER_ADD]) >= 2)
+        self.assertEqual(len([c for c in choices if c.link_type == FILTER_REMOVE]), 1)
+
+    def test_datetime_filter_month_range_selected(self):
+        params = MultiValueDict({'date_published':['1818-08..1818-09']})
+        f = DateTimeFilter('date_published', Book, params, max_links=10)
+        qs = Book.objects.all()
+
+        # Should get a number of books in queryset.
+        qs_filtered = f.apply_filter(qs)
+
+        start = date(1818, 8, 1)
+        end = date(1818, 10, 1)
+        self.assertEqual(list(qs_filtered),
+                         list(qs.filter(date_published__gte=start,
+                                        date_published__lt=end)))
+
+        # We only need 1 query if we've already told it what months to look at,
+        # and there is data for both months.
+        with self.assertNumQueries(1):
+            choices = f.get_choices(qs_filtered)
+
+        self.assertEqual(len([c for c in choices if c.link_type == FILTER_REMOVE]), 1)
+        # There are at least 2 books in this range, in different months
+        self.assertEqual(len([c for c in choices if c.link_type == FILTER_ADD]), 2)
+        self.assertEqual([c.label for c in choices if c.link_type == FILTER_ADD],
+                         ['August', 'September'])
+
     def test_datetime_filter_invalid_query(self):
         self.do_invalid_query_param_test(lambda params: DateTimeFilter('date_published', Book, params, max_links=10),
                                          MultiValueDict({'date_published':['1818xx']}))

django_easyfilters/tests/fixtures/django_easyfilters_tests.json

       "edition": 1, 
       "price": "44.99", 
       "binding": "C", 
-      "date_published": "1847-06-22", 
+      "date_published": "1847-10-22", 
       "authors": [
         7
       ], 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.