Browse Source

[search] Twitter results layout

Enrico Berti 13 years ago
parent
commit
fa75e97

+ 4 - 1
apps/search/src/search/forms.py

@@ -21,5 +21,8 @@ from django import forms
 
 
 class QueryForm(forms.Form):
 class QueryForm(forms.Form):
   query = forms.CharField(label='', max_length=256, required=False, initial='',
   query = forms.CharField(label='', max_length=256, required=False, initial='',
-                          widget=forms.TextInput(attrs={'class': 'input-xxlarge search-query', 'placeholder': 'e.g. solr or video'}))
+                          widget=forms.TextInput(attrs={'class': 'input-xxlarge search-query', 'placeholder': 'Search'}))
   fq = forms.CharField(label='', max_length=256, required=False, initial='', widget=forms.HiddenInput(), help_text='Solr Filter query')
   fq = forms.CharField(label='', max_length=256, required=False, initial='', widget=forms.HiddenInput(), help_text='Solr Filter query')
+  sort = forms.CharField(label='', max_length=256, required=False, initial='', widget=forms.HiddenInput(), help_text='Solr sort')
+  rows = forms.CharField(label='', required=False, initial='', widget=forms.HiddenInput(), help_text='Solr records per page')
+  start = forms.CharField(label='', required=False, initial='', widget=forms.HiddenInput(), help_text='Solr start record')

+ 170 - 20
apps/search/src/search/templates/index.mako

@@ -18,17 +18,124 @@
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 from itertools import izip
 from itertools import izip
+import math
 %>
 %>
 
 
 <%namespace name="macros" file="macros.mako" />
 <%namespace name="macros" file="macros.mako" />
 
 
 ${ commonheader(_('Search'), "search", user) }
 ${ commonheader(_('Search'), "search", user) }
 
 
+<style>
+  .content {
+    margin-left: 58px;
+  }
+
+  .action {
+    margin-right: 5px;
+  }
+
+  .action a, .time a, .account-group, .retweeted {
+    color: #999999;
+  }
+
+  .account-group a {
+    text-decoration: none;
+    font-weight: normal;
+  }
+
+  .username {
+    font-size: 12px;
+  }
+
+  .time {
+    color: #BBBBBB;
+    float: right;
+    margin-top: 1px;
+    position: relative;
+  }
+
+  .avatar {
+    position: absolute;
+    margin-left: -56px!important;
+    margin-top: 4px!important;
+    -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
+    -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
+    box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
+    -webkit-border-radius: 5px;
+    -moz-border-radius: 5px;
+    border-radius: 5px;
+  }
+
+  .text {
+    margin-bottom: 4px;
+    cursor: pointer;
+  }
+
+  .tweet-actions li {
+    display: inline;
+  }
+
+  .stream-item-footer {
+    font-size: 12px;
+    color: #999999;
+  }
+
+  ul.tweet-actions {
+    list-style: none outside none;
+  }
+
+  ul.tweet-actions {
+    margin: 0;
+    padding: 0;
+  }
+
+  .fullname {
+    color: #333333;
+    font-weight: bold;
+  }
+
+  .stream-item-footer, .retweeted {
+    font-size: 12px;
+    padding-top: 1px;
+  }
+
+  .icon {
+    background-position: 0 0;
+    background-repeat: no-repeat;
+    display: inline-block;
+    vertical-align: text-top;
+    height: 13px;
+    width: 14px;
+    margin-top: 0;
+    margin-left: -2px;
+  }
+  .icon-reply {
+    background-image: url("/search/static/art/reply.png");
+  }
+  .icon-retweet {
+    background-image: url("/search/static/art/retweet.png");
+  }
+  .twitter-logo {
+    background-image: url("/search/static/art/bird_gray_32.png");
+    width: 32px;
+    height: 32px;
+    background-repeat: no-repeat;
+    display: inline-block;
+    vertical-align: top;
+    margin-top: 2px;
+  }
+
+</style>
+
 <div class="container-fluid">
 <div class="container-fluid">
     <div class="row-fluid">
     <div class="row-fluid">
         <div class="span3">
         <div class="span3">
             <div class="well" style="padding: 8px 0;">
             <div class="well" style="padding: 8px 0;">
             <ul class="nav nav-list">
             <ul class="nav nav-list">
+                % if solr_query['fq']:
+                    <li class="nav-header">Current filter</li>
+                    <li style="margin-bottom: 20px"><a href="?query=${ solr_query['q'] }&sort=${solr_query["sort"]}">${solr_query['fq']} <i class="icon-trash"></i></a></li>
+                % endif
                 % if response and response['facet_counts']:
                 % if response and response['facet_counts']:
                     % if response['facet_counts']['facet_fields']:
                     % if response['facet_counts']['facet_fields']:
                         <h4>Fields</h4>
                         <h4>Fields</h4>
@@ -36,29 +143,23 @@ ${ commonheader(_('Search'), "search", user) }
                             % if response['facet_counts']['facet_fields'][cat]:
                             % if response['facet_counts']['facet_fields'][cat]:
                             <li class="nav-header">${cat}</li>
                             <li class="nav-header">${cat}</li>
                             % for subcat, count in macros.pairwise(response['facet_counts']['facet_fields'][cat]):
                             % for subcat, count in macros.pairwise(response['facet_counts']['facet_fields'][cat]):
-                                <li><a href="?query=${ solr_query['q'] }&fq=${ cat }:${ subcat }">${ subcat } (${ count })</a></li>
+                              %if count > 0 and subcat != "":
+                                <li><a href="?query=${ solr_query['q'] }&fq=${ cat }:${ subcat }&sort=${solr_query["sort"]}">${subcat} (${ count })</a></li>
+                              %endif
                             % endfor
                             % endfor
                             % endif
                             % endif
                         % endfor
                         % endfor
                     %endif
                     %endif
 
 
-                    % if response['facet_counts']['facet_queries']:
-                        ## Not used in demo
-                        <h4>Queries</h4>
-                        % for cat in response['facet_counts']['facet_queries']:
-                            % if response['facet_counts']['facet_queries'][cat]:
-                                <li><a href="#">${cat} (${response['facet_counts']['facet_queries'][cat]})</a></li>
-                            % endif
-                        % endfor
-                    %endif
-
                     % if response['facet_counts']['facet_ranges']:
                     % if response['facet_counts']['facet_ranges']:
                         <h4>Ranges</h4>
                         <h4>Ranges</h4>
                         % for cat in response['facet_counts']['facet_ranges']:
                         % for cat in response['facet_counts']['facet_ranges']:
                             % if response['facet_counts']['facet_ranges'][cat]:
                             % if response['facet_counts']['facet_ranges'][cat]:
                             <li class="nav-header">${cat}</li>
                             <li class="nav-header">${cat}</li>
                             % for range, count in macros.pairwise(response['facet_counts']['facet_ranges'][cat]['counts']):
                             % for range, count in macros.pairwise(response['facet_counts']['facet_ranges'][cat]['counts']):
-                                <li><a href="?query=${ solr_query['q'] }&fq=${ cat }:${ range }">${ range } (${ count })</a></li>
+                              %if count > 0:
+                                <li><a href="?query=${ solr_query['q'] }&fq=${ cat }:${ range }&sort=${solr_query["sort"]}">${ range } (${ count })</a></li>
+                              %endif
                             % endfor
                             % endfor
                             % endif
                             % endif
                         % endfor
                         % endfor
@@ -70,8 +171,8 @@ ${ commonheader(_('Search'), "search", user) }
                             % if response['facet_counts']['facet_dates'][cat]:
                             % if response['facet_counts']['facet_dates'][cat]:
                             <li class="nav-header">${cat}</li>
                             <li class="nav-header">${cat}</li>
                             % for date, count in response['facet_counts']['facet_dates'][cat].iteritems():
                             % for date, count in response['facet_counts']['facet_dates'][cat].iteritems():
-                              % if date not in ('start', 'end', 'gap'):
-                                <li><a href="?query=${ solr_query['q'] }&fq=${ cat }:${ date }">${ date } (${ count })</a></li>
+                              % if date not in ('start', 'end', 'gap') and count > 0:
+                                <li><a href="?query=${ solr_query['q'] }&fq=${ cat }:${ date }&sort=${solr_query["sort"]}">${ date } (${ count })</a></li>
                               % endif
                               % endif
                             % endfor
                             % endfor
                             % endif
                             % endif
@@ -83,8 +184,21 @@ ${ commonheader(_('Search'), "search", user) }
         </div>
         </div>
         <div class="span9">
         <div class="span9">
             <form class="form-search well">
             <form class="form-search well">
+                <i class="twitter-logo"></i>
                 ${ search_form }
                 ${ search_form }
                 <button class="btn" type="submit">Search</button>
                 <button class="btn" type="submit">Search</button>
+                <div class="btn-group pull-right">
+                  <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
+                    Sort by
+                    <span class="caret"></span>
+                  </a>
+                  <ul class="dropdown-menu">
+                    <li><a href="?query=${solr_query["q"]}&fq=${solr_query["fq"]}&sort=created_at+desc">Date</a></li>
+                    <li><a href="?query=${solr_query["q"]}&fq=${solr_query["fq"]}&sort=retweet_count+desc">Retweets count</a></li>
+                    <li class="divider"></li>
+                    <li><a href="?query=${solr_query["q"]}&fq=${solr_query["fq"]}">Reset sorting</a></li>
+                  </ul>
+                </div>
             </form>
             </form>
 
 
             % if response:
             % if response:
@@ -95,16 +209,52 @@ ${ commonheader(_('Search'), "search", user) }
                 % endfor
                 % endfor
                 </tbody>
                 </tbody>
                 </table>
                 </table>
-                <div class="row-fluid">
-                    Result founds: ${ response['response']['numFound'] }
-                </div>
+                  <div class="pagination">
+                    <ul class="pull-right">
+                      <%
+                        beginning = 0
+                        previous = int(solr_query["start"]) - int(solr_query["rows"])
+                        next = int(solr_query["start"]) + int(solr_query["rows"])
+                      %>
+                      %if int(solr_query["start"]) > 0:
+                        <li><a title="Beginning of List" href="?query=${solr_query["q"]}&fq=${solr_query["fq"]}&sort=${solr_query["sort"]}&rows=${solr_query["rows"]}&start=${beginning}">&larr; Beginning of List</a></li>
+                        <li><a title="Previous Page" href="?query=${solr_query["q"]}&fq=${solr_query["fq"]}&sort=${solr_query["sort"]}&rows=${solr_query["rows"]}&start=${previous}">Previous Page</a></li>
+                      %endif
+                      <li><a title="Next page" href="?query=${solr_query["q"]}&fq=${solr_query["fq"]}&sort=${solr_query["sort"]}&rows=${solr_query["rows"]}&start=${next}">Next Page</a></li>
+                    </ul>
+                    <p>Show
+                      <select id="recordsPerPage" class="input-mini"><option value="15">15</option><option value="30">30</option><option value="45">45</option><option value="60">60</option><option value="100">100</option></select>
+                      tweets per page.
+                      Showing ${int(solr_query["start"])+1} to ${int(solr_query["start"])+int(solr_query["rows"])} of ${ response['response']['numFound'] } tweets
+                    </p>
+                  </div>
             % endif
             % endif
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
 
 
-<div class="hide">
-    ${ rr }
-</div>
+<script src="/static/ext/js/moment.min.js" type="text/javascript" charset="utf-8"></script>
+<script>
+  $(document).ready(function(){
+    $("a[data-dt]").each(function(){
+      $(this).text(moment(new Date($(this).data("dt"))).fromNow());
+    });
+    $(".text").click(function(e){
+      if ($(e.target).is("div")){
+        window.open($(this).data("link"))
+      }
+      if ($(e.target).is("a")){
+        e.stopImmediatePropagation();
+        e.stopPropagation();
+      }
+    });
+    $("[rel='popover']").popover();
+    $("#recordsPerPage").change(function(){
+      $("input[name='rows']").val($(this).val());
+      $("input[name='rows']").closest("form").submit();
+    });
+    $("#recordsPerPage").val($("input[name='rows']").val());
+  });
+</script>
 
 
 ${ commonfooter(messages) }
 ${ commonfooter(messages) }

+ 181 - 26
apps/search/src/search/templates/macros.mako

@@ -16,8 +16,140 @@
 <%!
 <%!
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 from itertools import izip
 from itertools import izip
-%>
+import re
+import urllib
+
+# <http://github.com/mzsanford/twitter-text-java>
+
+AT_SIGNS = ur'[@\uff20]'
+UTF_CHARS = ur'a-z0-9_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff'
+SPACES = ur'[\u0020\u00A0\u1680\u180E\u2002-\u202F\u205F\u2060\u3000]'
+
+LIST_PRE_CHARS = ur'([^a-z0-9_]|^)'
+LIST_END_CHARS = ur'([a-z0-9_]{1,20})(/[a-z][a-z0-9\x80-\xFF-]{0,79})?'
+LIST_REGEX = re.compile(LIST_PRE_CHARS + '(' + AT_SIGNS + '+)' + LIST_END_CHARS, re.IGNORECASE)
+
+USERNAME_REGEX = re.compile(ur'\B' + AT_SIGNS + LIST_END_CHARS, re.IGNORECASE)
+REPLY_REGEX = re.compile(ur'^(?:' + SPACES + ur')*' + AT_SIGNS + ur'([a-z0-9_]{1,20}).*', re.IGNORECASE)
+
+HASHTAG_EXP = ur'(^|[^0-9A-Z&/]+)(#|\uff03)([0-9A-Z_]*[A-Z_]+[%s]*)' % UTF_CHARS
+HASHTAG_REGEX = re.compile(HASHTAG_EXP, re.IGNORECASE)
+
+PRE_CHARS = ur'(?:[^/"\':!=]|^|\:)'
+DOMAIN_CHARS = ur'([\.-]|[^\s_\!\.\/])+\.[a-z]{2,}(?::[0-9]+)?'
+PATH_CHARS = ur'(?:[\.,]?[%s!\*\'\(\);:=\+\$/%s#\[\]\-_,~@])' % (UTF_CHARS, '%')
+QUERY_CHARS = ur'[a-z0-9!\*\'\(\);:&=\+\$/%#\[\]\-_\.,~]'
+
+PATH_ENDING_CHARS = r'[%s\)=#/]' % UTF_CHARS
+QUERY_ENDING_CHARS = '[a-z0-9_&=#]'
+
+URL_REGEX = re.compile('((%s)((https?://|www\\.)(%s)(\/%s*%s?)?(\?%s*%s)?))'
+                     % (PRE_CHARS, DOMAIN_CHARS, PATH_CHARS,
+                        PATH_ENDING_CHARS, QUERY_CHARS, QUERY_ENDING_CHARS), re.IGNORECASE)
+
+IANA_ONE_LETTER_DOMAINS = ('x.com', 'x.org', 'z.com', 'q.net', 'q.com', 'i.net')
+
+
+MAX_URL_LENGTH = 30
+
+def parse_urls(match):
+      mat = match.group(0)
+
+      # Fix a bug in the regex concerning www...com and www.-foo.com domains
+      domain = match.group(5)
+      if domain[0] in '.-':
+          return mat
+
+      # Only allow IANA one letter domains that are actually registered
+      if len(domain) == 5 \
+         and domain[-4:].lower() in ('.com', '.org', '.net') \
+         and not domain.lower() in IANA_ONE_LETTER_DOMAINS:
+
+          return mat
+
+      # Check for urls without http(s)
+      pos = mat.find('http')
+      if pos != -1:
+          pre, url = mat[:pos], mat[pos:]
+          full_url = url
+
+      # Find the www and force http://
+      else:
+          pos = mat.lower().find('www')
+          pre, url = mat[:pos], mat[pos:]
+          full_url = 'http://%s' % url
+
+      return '%s%s' % (pre, format_url(full_url, shorten_url(escape(url))))
+
+def parse_users(match):
+      # Don't parse lists here
+      if match.group(2) is not None:
+          return match.group(0)
+
+      mat = match.group(0)
 
 
+      return format_username(mat[0:1], mat[1:])
+
+def parse_lists(match):
+      # Don't parse usernames here
+      if match.group(4) is None:
+          return match.group(0)
+
+      pre, at_char, user, list_name = match.groups()
+      list_name = list_name[1:]
+
+      return '%s%s' % (pre, format_list(at_char, user, list_name))
+
+def parse_tags(match):
+      mat = match.group(0)
+
+      # Fix problems with the regex capturing stuff infront of the #
+      tag = None
+      for i in u'#\uff03':
+          pos = mat.rfind(i)
+          if pos != -1:
+              tag = i
+              break
+
+      pre, text = mat[:pos], mat[pos + 1:]
+
+      return '%s%s' % (pre, format_tag(tag, text))
+
+def shorten_url(text):
+    if len(text) > MAX_URL_LENGTH and MAX_URL_LENGTH != -1:
+      text = text[0:MAX_URL_LENGTH - 3]
+      amp = text.rfind('&')
+      close = text.rfind(';')
+      if amp != -1 and (close == -1 or close < amp):
+        text = text[0:amp]
+      return text + '...'
+    else:
+      return text
+
+def format_tag(tag, text):
+    return '<a href="http://search.twitter.com/search?q=%s" target="_blank">%s%s</a>' \
+              % (urllib.quote('#' + text.encode('utf-8')), tag, text)
+
+def format_username(at_char, user):
+    return '<a href="http://twitter.com/%s" target="_blank">%s%s</a>' \
+             % (user, at_char, user)
+
+def format_list(at_char, user, list_name):
+    return '<a href="http://twitter.com/%s/%s" target="_blank">%s%s/%s</a>' \
+             % (user, list_name, at_char, user, list_name)
+
+def format_url(url, text):
+    return '<a href="%s" target="_blank">%s</a>' % (escape(url), text)
+
+def highlight(word, text):
+    search = re.compile(r'\b(%s)\b' % word, re.I)
+    return search.sub('<strong>\\1</strong>', text)
+
+def escape(text)  :
+  return ''.join({'&': '&amp;', '"': '&quot;',
+                  '\'': '&apos;', '>': '&gt;',
+                  '<': '&lt;'}.get(c, c) for c in text)
+%>
 
 
 
 
 <%def name="pairwise(iterable)">
 <%def name="pairwise(iterable)">
@@ -28,39 +160,62 @@ from itertools import izip
   %>
   %>
 </%def>
 </%def>
 
 
+<%def name="parseLinks(text)">
+  <%
+    html = URL_REGEX.sub(parse_urls, text)
+    html = USERNAME_REGEX.sub(parse_users, html)
+    html = LIST_REGEX.sub(parse_lists, html)
+    html = HASHTAG_REGEX.sub(parse_tags, html)
+    if solr_query['q']:
+      html = highlight(solr_query['q'], html)
+    return html
+  %>
+</%def>
+
 
 
 <%def name="tweet_result(result)">
 <%def name="tweet_result(result)">
 <tr>
 <tr>
     <td style="word-wrap: break-word;">
     <td style="word-wrap: break-word;">
-        ## TODO on hover username popover
-        <div id="${ 'user-%s' % result.get('id', '') }" class="hide">
-            ${ result.get('user_screen_name', '') } @${ result.get('user_name', '') }
 
 
-            Description: ${ result.get('user_description', '') }
-            Location: ${ result.get('user_location', '') }
-
-            User tweets #: ${ result.get('user_statuses_count', '') }
-            User followers #: ${ result.get('user_followers_count', '') }
+      <div class="content">
+        <div class="stream-item-header">
+          <small class="time">
+            <a href="https://twitter.com/${ result.get('user_screen_name', '') }/status/${ result.get('id', '') }" target="_blank" data-dt="${ result.get('created_at', '') }"></a>
+          </small>
+          <a target="_blank" href="https://twitter.com/${ result.get('user_screen_name', '') }" class="account-group">
+            <img title="${ result.get('user_name', '') }" src="http://twitter.com/api/users/profile_image/${ result.get('user_screen_name', '') }" class="avatar"
+                 data-placement="left" rel="popover"  data-content="Location: ${ result.get('user_location', '') }<br/>User tweets #: ${ result.get('user_statuses_count', '') }<br/>User followers #: ${ result.get('user_followers_count', '') }" data-original-title="@${ result.get('user_screen_name', '') }" data-trigger="hover">
+            <strong class="fullname">${ result.get('user_name', '') }</strong>
+            <span>&rlm;</span><span class="username">@${ result.get('user_screen_name', '') }</span>
+          </a>
+        </div>
+        <div class="text" data-link="https://twitter.com/${ result.get('user_screen_name', '') }/status/${ result.get('id', '') }">
+          ${ parseLinks(result.get('text', '')) }
+          %if result.get('retweet_count', ''):
+              <div class="retweeted">
+                Retweeted ${ result.get('retweet_count', '') } times
+              </div>
+          %endif
         </div>
         </div>
 
 
-        <a rel="nofollow" href="https://twitter.com/${ result.get('user_screen_name', '') }"><b>${ result.get('user_name', '') }</b></a> @${ result.get('user_name', '') }
-        <p>${ result.get('text', '') }</p>
-        <p>
-        % if result.get('features'):
-            <ul>
-            % for feature in result.get('features', ''):
-                <li>${ feature }</li>
-            % endfor
-            </ul>
-        % endif
-
-
-        </p>
-        <div style="color: #46a546;">
-            Retweet: ${ result.get('retweet_count', '') }
-            Location: ${ result.get('user_location', 'Unknown') or 'Unknown' }
-            Date ${ result.get('created_at', '') }
+        <div class="stream-item-footer">
+          <ul class="tweet-actions">
+            <li class="action">
+              <a href="https://twitter.com/intent/tweet?in_reply_to=${ result.get('id', '') }" target="_blank">
+                <i class="icon icon-reply"></i>
+                <b>Reply</b>
+              </a>
+            </li>
+            <li class="action">
+              <a href="https://twitter.com/intent/retweet?tweet_id=${ result.get('id', '') }" target="_blank">
+                <i class="icon icon-retweet"></i>
+                <b>Retweet</b>
+              </a>
+            </li>
+          </ul>
         </div>
         </div>
+      </div>
+    </div>
     </td>
     </td>
 </tr>
 </tr>
 </%def>
 </%def>

+ 11 - 1
apps/search/src/search/views.py

@@ -29,7 +29,7 @@ from desktop.lib.rest.resource import Resource
 
 
 
 
 # http://lucene.apache.org/solr/api-4_0_0-BETA/doc-files/tutorial.html#Getting+Started
 # http://lucene.apache.org/solr/api-4_0_0-BETA/doc-files/tutorial.html#Getting+Started
-SOLR_URL = 'http://localhost:8983/solr/'
+SOLR_URL = 'http://c1328.hal.cloudera.com:8983/solr/'
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
@@ -42,6 +42,9 @@ def index(request):
     solr_query = {}
     solr_query = {}
     solr_query['q'] = search_form.cleaned_data['query']
     solr_query['q'] = search_form.cleaned_data['query']
     solr_query['fq'] = search_form.cleaned_data['fq']
     solr_query['fq'] = search_form.cleaned_data['fq']
+    solr_query['sort'] = search_form.cleaned_data['sort']
+    solr_query['rows'] = search_form.cleaned_data['rows'] or 15
+    solr_query['start'] = search_form.cleaned_data['start'] or 0
     response = SolrApi(SOLR_URL).query(solr_query)
     response = SolrApi(SOLR_URL).query(solr_query)
     response = json.loads(response)
     response = json.loads(response)
 
 
@@ -59,6 +62,9 @@ class SolrApi(object):
       return self._root.get('collection1/browse', (('q', solr_query['q']),
       return self._root.get('collection1/browse', (('q', solr_query['q']),
                                                    ('fq', solr_query['fq']),
                                                    ('fq', solr_query['fq']),
                                                    ('wt', 'json'),
                                                    ('wt', 'json'),
+                                                   ('sort', solr_query['sort']),
+                                                   ('rows', solr_query['rows']),
+                                                   ('start', solr_query['start']),
 
 
                                                    ('facet', 'true'),
                                                    ('facet', 'true'),
                                                    ('facet.limit', 10),
                                                    ('facet.limit', 10),
@@ -78,3 +84,7 @@ class SolrApi(object):
     except RestException, e:
     except RestException, e:
       print e
       print e
       return '{"responseHeader":{"status":0,"QTime":1,"params":{"wt":"json"}},"response":{"numFound":0,"start":0,"maxScore":0.0,"docs":[]},"highlighting":{}}'
       return '{"responseHeader":{"status":0,"QTime":1,"params":{"wt":"json"}},"response":{"numFound":0,"start":0,"maxScore":0.0,"docs":[]},"highlighting":{}}'
+
+
+
+

BIN
apps/search/static/art/bird_gray_32.png


BIN
apps/search/static/art/reply.png


BIN
apps/search/static/art/retweet.png