Merge branch 'master' of git://github.com/melinath/philo
authorJoseph Spiros <joseph.spiros@ithinksw.com>
Wed, 13 Apr 2011 20:29:29 +0000 (16:29 -0400)
committerJoseph Spiros <joseph.spiros@ithinksw.com>
Wed, 13 Apr 2011 20:29:29 +0000 (16:29 -0400)
* 'master' of git://github.com/melinath/philo:
  Added 1.2.X compatibility to shipherd LazyNavigationRecurser.
  Minor tweaks to sobol result handling.
  Minor corrections and improvements to sobol.
  Added crude double-import detection to search registration. Added Django 1.2 fallback to SlugMultipleChoiceField formfield method.
  Fixed a sobol bug which was causing search views to always use all available searches. Added some help text to the ajax api boolean.
  Modified the get_start and get_end methods to return datetimes if times are specified.

contrib/julian/models.py
contrib/shipherd/templatetags/shipherd.py
contrib/sobol/models.py
contrib/sobol/search.py
models/fields/__init__.py

index 5dea7a3..5c49c7e 100644 (file)
@@ -79,10 +79,10 @@ class TimedModel(models.Model):
                        raise ValidationError("A %s cannot end before it starts." % self.__class__.__name__)
        
        def get_start(self):
-               return self.start_date
+               return datetime.datetime.combine(self.start_date, self.start_time) if self.start_time else self.start_date
        
        def get_end(self):
-               return self.end_date
+               return datetime.datetime.combine(self.end_date, self.end_time) if self.end_time else self.end_date
        
        class Meta:
                abstract = True
index 1413bdf..6e36126 100644 (file)
@@ -1,4 +1,4 @@
-from django import template
+from django import template, VERSION as django_version
 from django.conf import settings
 from django.utils.safestring import mark_safe
 from philo.contrib.shipherd.models import Navigation
@@ -60,6 +60,10 @@ class LazyNavigationRecurser(object):
                        context['item'] = item
                        context['children'] = self.__class__(self.template_nodes, item.get_children(), context, request)
                        
+                       # Django 1.2.X compatibility - a lazy recurser will not be called if accessed as a template variable.
+                       if django_version < (1,3):
+                               context['children'] = context['children']()
+                       
                        # Then render the nodelist bit by bit.
                        for node in self.template_nodes:
                                bits.append(node.render(context))
index b653c09..e4e4202 100644 (file)
@@ -130,13 +130,13 @@ class Click(models.Model):
 class SearchView(MultiView):
        results_page = models.ForeignKey(Page, related_name='search_results_related')
        searches = SlugMultipleChoiceField(choices=registry.iterchoices())
-       enable_ajax_api = models.BooleanField("Enable AJAX API", default=True)
+       enable_ajax_api = models.BooleanField("Enable AJAX API", default=True, help_text="Search results will be available <i>only</i> by AJAX, not as template variables.")
        placeholder_text = models.CharField(max_length=75, default="Search")
        
        search_form = SearchForm
        
        def __unicode__(self):
-               return u"%s (%s)" % (self.placeholder_text, u", ".join([display for slug, display in registry.iterchoices()]))
+               return u"%s (%s)" % (self.placeholder_text, u", ".join([display for slug, display in registry.iterchoices() if slug in self.searches]))
        
        def get_reverse_params(self, obj):
                raise ViewCanNotProvideSubpath
@@ -198,7 +198,7 @@ class SearchView(MultiView):
                                        })
                                else:
                                        context.update({
-                                               'searches': [{'verbose_name': verbose_name, 'url': self.reverse('ajax_api_view', kwargs={'slug': slug}, node=request.node)} for slug, verbose_name in registry.iterchoices()]
+                                               'searches': [{'verbose_name': verbose_name, 'url': self.reverse('ajax_api_view', kwargs={'slug': slug}, node=request.node)} for slug, verbose_name in registry.iterchoices() if slug in self.searches]
                                        })
                else:
                        form = SearchForm()
index 36c2b5d..8c695c6 100644 (file)
@@ -25,9 +25,10 @@ __all__ = (
 
 SEARCH_CACHE_KEY = 'philo_sobol_search_results'
 DEFAULT_RESULT_TEMPLATE_STRING = "{% if url %}<a href='{{ url }}'>{% endif %}{{ title }}{% if url %}</a>{% endif %}"
+DEFAULT_RESULT_TEMPLATE = Template(DEFAULT_RESULT_TEMPLATE_STRING)
 
 # Determines the timeout on the entire result cache.
-MAX_CACHE_TIMEOUT = 60*60*24*7
+MAX_CACHE_TIMEOUT = 60*24*7
 
 
 class RegistrationError(Exception):
@@ -42,8 +43,9 @@ class SearchRegistry(object):
        def register(self, search, slug=None):
                slug = slug or search.slug
                if slug in self._registry:
-                       if self._registry[slug] != search:
-                               raise RegistrationError("A different search is already registered as `%s`")
+                       registered = self._registry[slug]
+                       if registered.__module__ != search.__module__:
+                               raise RegistrationError("A different search is already registered as `%s`" % slug)
                else:
                        self._registry[slug] = search
        
@@ -93,7 +95,10 @@ class Result(object):
                return self.search.get_result_title(self.result)
        
        def get_url(self):
-               return "?%s" % self.search.get_result_querydict(self.result).urlencode()
+               qd = self.search.get_result_querydict(self.result)
+               if qd is None:
+                       return ""
+               return "?%s" % qd.urlencode()
        
        def get_template(self):
                return self.search.get_result_template(self.result)
@@ -170,7 +175,7 @@ class BaseSearch(object):
                                        limit = self.result_limit
                                        if limit is not None:
                                                limit += 1
-                                       results = self.get_results(self.result_limit)
+                                       results = self.get_results(limit)
                                except:
                                        if settings.DEBUG:
                                                raise
@@ -209,13 +214,16 @@ class BaseSearch(object):
                raise NotImplementedError
        
        def get_result_querydict(self, result):
-               return make_tracking_querydict(self.search_arg, self.get_result_url(result))
+               url = self.get_result_url(result)
+               if url is None:
+                       return None
+               return make_tracking_querydict(self.search_arg, url)
        
        def get_result_template(self, result):
                if hasattr(self, 'result_template'):
                        return loader.get_template(self.result_template)
                if not hasattr(self, '_result_template'):
-                       self._result_template = Template(DEFAULT_RESULT_TEMPLATE_STRING)
+                       self._result_template = DEFAULT_RESULT_TEMPLATE
                return self._result_template
        
        def get_result_extra_context(self, result):
@@ -244,9 +252,6 @@ class BaseSearch(object):
 class DatabaseSearch(BaseSearch):
        model = None
        
-       def has_more_results(self):
-               return self.get_queryset().count() > self.result_limit
-       
        def search(self, limit=None):
                if not hasattr(self, '_qs'):
                        self._qs = self.get_queryset()
@@ -298,7 +303,7 @@ class GoogleSearch(JSONSearch):
        query_format_str = "?v=1.0&q=%s"
        # TODO: Change this template to reflect the app's actual name.
        result_template = 'search/googlesearch.html'
-       timeout = 60
+       _cache_timeout = 60
        
        def parse_response(self, response, limit=None):
                responseData = json.loads(response.read())['responseData']
@@ -361,7 +366,7 @@ else:
                def parse_response(self, response, limit=None):
                        strainer = self.strainer
                        soup = BeautifulSoup(response, parseOnlyThese=strainer)
-                       return self.parse_results(soup[:limit])
+                       return self.parse_results(soup.findAll(recursive=False, limit=limit))
                
                def parse_results(self, results):
                        """
@@ -378,5 +383,5 @@ else:
                
                def parse_response(self, response, limit=None):
                        strainer = self.strainer
-                       soup = BeautifulStoneSoup(page, selfClosingTags=self._self_closing_tags, parseOnlyThese=strainer)
-                       return self.parse_results(soup[:limit])
\ No newline at end of file
+                       soup = BeautifulStoneSoup(response, selfClosingTags=self._self_closing_tags, parseOnlyThese=strainer)
+                       return self.parse_results(soup.findAll(recursive=False, limit=limit))
\ No newline at end of file
index 1f9603e..d900e31 100644 (file)
@@ -109,7 +109,8 @@ class SlugMultipleChoiceField(models.Field):
                                del kwargs[k]
                
                defaults.update(kwargs)
-               form_class = forms.TypedMultipleChoiceField
+               # Django 1.2 does not supply MultipleChoiceField
+               form_class = getattr(forms, 'TypedMultipleChoiceField', forms.MultipleChoiceField)
                return form_class(**defaults)
        
        def validate(self, value, model_instance):