Commits

Anonymous committed 71433d5

date range support and multiple sort fields

  • Participants
  • Parent commits cd23bd4

Comments (0)

Files changed (4)

         "gender" : {"display" : "Gender"},
         "fte" : {"display" : "FTE"},
         "phd_supervisions" : {"display" : "Total PhD Students"},
-        "publication_date" : {"display" : "Publication Date"}
+        "funding_code" : {"display" : "Funding Code"},
+        "primary_position" : {"display" : "Position"}
     },
     
     # The fields which are used for straight ranged faceting (dates should be dealt with
             "min" : 0, "max" : 8, "gap" : 2, "mincount" : 2, "infinity" : 10
         }
     },
-    "sort_fields" : ["fte"],
-    "display_facet_order" : ["publication_date", "org_unit_name", "gender", "fte", 
+    
+    "facet_dates" : {
+        "contract_end" : {
+            "display" : "Contract End",
+            "min" : "1850-01-01T00:00:00Z", "max" : "1910-01-01T00:00:00Z" , "gap" : "+1YEAR", "mincount" : 2
+        },
+        "publication_date" : {
+            "display" : "Publication Year",
+            "min" : "1850-01-01T00:00:00Z", "max" : "1910-01-01T00:00:00Z", "gap" : "+1YEAR", "mincount" : 2
+        }
+    },
+    
+    "display_facet_order" : ["publication_date", "org_unit_name", "gender", "fte", "contract_end",
+                                "funding_code", "primary_position",
                                 "phd_supervisions", "journal_count", "book_count", 
                                 "chapter_count", "conference_count"],
+    
+    "sort_fields" : {
+        "primary_department" : {"display" : "Department"}, 
+        "contract_end" : {"display" : "Contract End"}, 
+        "name" : {"display" : "Name"},
+        "fte" : {"display" : "FTE"}
+    },
+    
     "facet_value_functions" : {
         "gender" : {"value_map" : {"M" : "Male", "F" : "Female"}},
-        "publication_date" : {"regex_map" : {"expression" : "([\\d]{4})-.*"}}
+        "publication_date" : {"regex_map" : {"expression" : "([\\d]{4})-.*"}},
+        "contract_end" : {"regex_map" : {"expression" : "([\\d]{4})-.*"}}
+    },
+    
+    "upper_display_functions" : {
+        "publication_date" : {"years_different" : {}},
+        "contract_end" : {"years_different" : {}}
     },
     
     "display_fields" : {
         "phd_supervisions" : "Total PhD Students",
         "supervising" : "Supervising",
         "publication_id" : "Publication ID",
-        "publication_date" : "Publication Date"
+        "publication_date" : "Publication Date",
+        "primary_department" : "Department",
+        "primary_position" : "Position",
+        "contract_end" : "Contract End",
+        "funding_code" : "Funding Code"
     },
-    "display_field_order" : ["org_unit_name", "name", "gender", "fte", "phd_supervisions", 
-                                "journal_count", "book_count", "chapter_count", "conference_count"]
+    "display_field_order" : ["primary_department", "primary_position", "name", "gender", "fte", "contract_end", "funding_code",
+                                "phd_supervisions", "journal_count", "book_count", "chapter_count", "conference_count"]
 }
-import web, solr, os, json, urllib2, operator, re
+import web, solr, os, json, urllib2, operator, re, unicodedata
 from mako.template import Template
 from mako.runtime import Context
 from mako.lookup import TemplateLookup
 from StringIO import StringIO
 from copy import deepcopy
 from pkg_resources import resource_stream
+from datetime import datetime, timedelta
+import logging
+
+logging.basicConfig(level=logging.DEBUG)
 
 class SolrEyesController(object):
     def GET(self):
         self.config = config
         self.args = args if args is not None else self.config.get_default_args()
 
+    def current_sort_order(self):
+        if not self.args.has_key('sort'):
+            return []
+        return self.args['sort']
+    
+    def current_sort_fields(self):
+        if not self.args.has_key('sort'):
+            return []
+        return [k for k, v in self.args['sort']]
+
     def get_ordered_facets(self, facet):
         if facet in self.config.facet_fields:
             return self._sort_facets_by_count(self.results.facet_counts['facet_fields'][facet])
         elif facet in self.config.facet_ranges.keys():
             dict = self._get_range_dict(facet)
             return self._sort_facets_by_range(dict)
+        elif facet in self.config.facet_dates.keys():
+            dict = self._get_date_dict(facet)
+            return self._sort_facets_by_range(dict)
     
     def in_args(self, facet, value=None):
         if value is not None:
             return len(self.results.facet_counts['facet_fields'][facet]) > 0
         elif facet in self.config.facet_ranges.keys():
             return len(self.results.facet_counts['facet_ranges'][facet]["counts"]) > 0
+        elif facet in self.config.facet_dates.keys():
+            return len(self.results.facet_counts['facet_dates'][facet]) > 0
         return False
     
     def is_start(self):
     def set(self):
         return self.results.results
     
+    def page_size(self):
+        return self.args['rows']
+    
+    def get_str(self, result, field):
+        if hasattr(result.get(field), "append"):
+            return ", ".join([self.asciify(val) for val in result.get(field)])
+        else:
+            return self.asciify(result.get(field))
+    
+    def asciify(self, string):
+        return unicodedata.normalize('NFKD', unicode(string)).encode('ascii','ignore')
+    
     def first_page_end(self):
         return self.args['rows']
     
             r = (lower, upper)
             rdict[r] = dict[str(lower)]
         return rdict
+        
+    def _get_date_dict(self, facet):
+        dict = self.results.facet_counts['facet_dates'][facet]
+        keys = [(datetime.strptime(d, "%Y-%m-%dT%H:%M:%SZ"), d) for d in dict.keys() if d[0:2].isdigit()]
+        keys = sorted(keys, key=operator.itemgetter(0))
+        rdict = {}
+        for i in range(len(keys)):
+            lower, sl = keys[i]
+            upper = -1
+            if i < len(keys) - 1:
+                upper, su = keys[i+1]
+                upper = upper - timedelta(seconds=1)
+            r = (lower, upper)
+            rdict[r] = dict[sl]
+        return rdict
     
     def _sort_facets_by_count(self, dict):
         return sorted(dict.iteritems(), key=operator.itemgetter(1), reverse=True)
             del myargs['start']
         j = json.dumps(myargs)
         return self.config.base_url + "?a=" + urllib2.quote(j)
+        
+    def get_add_date_url(self, field, value, upper=None):
+        myargs = deepcopy(self.args)
+        value = '{0.year}-{0.month:{1}}-{0.day:{1}}T{0.hour:{1}}:{0.minute:{1}}:{0.second:{1}}Z'.format(value, '02')
+        if upper is not None and upper != -1:
+            upper = '{0.year}-{0.month:{1}}-{0.day:{1}}T{0.hour:{1}}:{0.minute:{1}}:{0.second:{1}}Z'.format(upper, '02')
+        if myargs["q"].has_key(field):
+            if upper is None and value not in myargs["q"][field]:
+                myargs["q"][field].append(value)
+            elif upper is not None:
+                myargs["q"][field] = [value, upper]
+        else:
+            if upper is None:
+                myargs["q"][field] = [value]
+            else:
+                myargs["q"][field] = [value, upper]
+        if myargs.has_key('start'):
+            del myargs['start']
+        j = json.dumps(myargs)
+        return self.config.base_url + "?a=" + urllib2.quote(j)
 
     def get_delete_url(self, field, value=None):
         myargs = deepcopy(self.args)
         
     def get_sort_url(self, field, direction):
         myargs = deepcopy(self.args)
-        myargs["sort"] = {field : direction}
+        if myargs.has_key("sort"):
+            isnew = True
+            for i in range(len(myargs['sort'])):
+                f, d = myargs['sort'][i]
+                if f == field:
+                    myargs['sort'][i] = [field, direction]
+                    isnew = False
+                    break
+            if isnew:
+                myargs['sort'].append([field, direction])
+        else:
+            myargs["sort"] = [[field, direction]]
+        j = json.dumps(myargs)
+        return self.config.base_url + "?a=" + urllib2.quote(j)
+        
+    def get_unsort_url(self, field):
+        myargs = deepcopy(self.args)
+        for i in range(len(myargs['sort'])):
+            f, direction = myargs['sort'][i]
+            if f == field:
+                del myargs['sort'][i]
+                break
         j = json.dumps(myargs)
         return self.config.base_url + "?a=" + urllib2.quote(j)
         
     def _do_query(self, args, arg_separator="__"):
         # we build all our solr args in one dictionary for convenience
         solr_args = {}
+        solr_args['facet'] = "on"
         
         # Except the fields required explicitly by the solrpy api:
         
                 if limit == -1:
                     limit = self.config.facet_ranges[field]["infinity"]
                 qs.append(field + ":[" + str(args["q"][field][0]) + " TO " + str(limit) + "]")
+            elif field in self.config.facet_dates.keys():
+                limit = args['q'][field][1]
+                if limit == -1:
+                    now = datetime.today().strftime("%Y-%m-%dT%H:%M:%SZ")
+                    limit = self.config.facet_dates[field].get("inifinity", now)
+                qs.append(field + ":[" + str(args["q"][field][0]) + " TO " + str(limit) + "]")
+            elif field == "*":
+                for value in args['q'][field]:
+                    qs.append(field + ":" + value)
         q = " AND ".join(qs)
         
         # start position (for paging)
             if args["facet_range"][f].has_key("gap"):
                 solr_args["f." + f + ".facet.range.gap"] = args["facet_range"][f]["gap"]
         
+        # set up the date range parameters
+        for f in args["facet_date"].keys():
+            if solr_args.has_key("facet.date"):
+                solr_args["facet.date"].append(f)
+            else:
+                solr_args["facet.date"] = [f]
+            
+            if args["facet_date"][f].has_key("mincount"):
+                solr_args["f." + f + ".facet.mincount"] = args["facet_date"][f]["mincount"]
+            
+            if args["facet_date"][f].has_key("min"):
+                solr_args["f." + f + ".facet.date.start"] = args["facet_date"][f]["min"]
+                
+            if args["facet_date"][f].has_key("max"):
+                solr_args["f." + f + ".facet.date.end"] = args["facet_date"][f]["max"]
+            else:
+                solr_args["f." + f + ".facet.date.end"] = datetime.today().strftime("%Y-%m-%dT%H:%M:%SZ")
+                
+            if args["facet_date"][f].has_key("gap"):
+                solr_args["f." + f + ".facet.date.gap"] = args["facet_date"][f]["gap"]
+        
         # facet mincount from config
         solr_args["facet.mincount"] = self.config.facet_mincount
         
         # sort options
         if args.has_key("sort"):
             sort_parts = []
-            for sort_field, direction in args["sort"].iteritems():
+            for sort_field, direction in args['sort']:
                 sort_parts.append(sort_field + " " + direction)
             solr_args["sort"] = ", ".join(sort_parts)
         
         # solrpy convert the keywordargs
         solrpy_args = self.solrpyise(solr_args, arg_separator)
         
-        print solrpy_args
-        
-        return self.select(q=q, **solrpy_args)
+        results = self.select(q=q, **solrpy_args)
+        return results
     
     def solrpyise(self, dict, arg_separator="__"):
         edict = {}
             self.facet_display_values[field] = props["display"]
         for field, props in self.cfg['facet_ranges'].iteritems():
             self.facet_display_values[field] = props["display"]
+        for field, props in self.cfg['facet_dates'].iteritems():
+            self.facet_display_values[field] = props["display"]
         
     def __getattr__(self, attr):
         return self.cfg.get(attr, None)
     def get_default_args(self):
         fields = deepcopy(self.facet_fields.keys())
         ranges = deepcopy(self.facet_ranges)
+        dates = deepcopy(self.facet_dates)
         for k, v in ranges.iteritems():
             del v['display']
+        for k, v in dates.iteritems():
+            del v['display']
         return {
             "q" : {},
             "start" : 0,
             "facet_field" : fields,
             "facet_range" : ranges,
+            "facet_date" : dates,
             "rows" : self.default_results_per_page
         }
         
             
     def get_field_name(self, field):
         return self.display_fields.get(field, field)
+        
+    def display_upper(self, facet, lower, upper):
+        if self.upper_display_functions.has_key(facet):
+            d = self.upper_display_functions[facet]
+            func_name = d.keys()[0]
+            args = d[func_name]
+            func = globals()[func_name]
+            return func(lower, upper, args)
+        else:
+            return True
 
 def value_map(value, d):
     return d.get(value, value)
     if len(capture) == 1:
         return capture[0]
     return value
+
+def years_different(lower, upper, dict):
+    lyear = regex_map(lower, {"expression" : "([\\d]{4})-.*"})
+    uyear = regex_map(upper, {"expression" : "([\\d]{4})-.*"})
+    return lyear != uyear

File templates/solreyes.mako

                         </a><br/>
     %               endfor
     %           endif
+    %       elif facet in c['config'].facet_dates.keys():
+    
+    %           if c['results'].in_args(facet):
+    %               if c['results'].get_selected_range_end(facet) != -1:
+                        <em>
+                            ${c['config'].get_value_display(facet, str(c['results'].get_selected_range_start(facet)))}
+        %               if c['config'].display_upper(facet, str(c['results'].get_selected_range_start(facet)), str(c['results'].get_selected_range_end(facet))):
+                            - 
+                            ${c['config'].get_value_display(facet, str(c['results'].get_selected_range_end(facet)))}
+        %               endif
+                        (${c['results'].numFound()})
+                        </em>
+                        &nbsp;&nbsp;<a href="${c['url_manager'].get_delete_url(facet)}">x</a><br/>
+    %               else:
+                        <em>${c['config'].get_value_display(facet, str(c['results'].get_selected_range_start(facet)))}+
+                        (${c['results'].numFound()})
+                        </em>&nbsp;&nbsp;<a href="${c['url_manager'].get_delete_url(facet)}">x</a><br/>
+    %               endif
+    %           else:
+    %               for lower, upper, count in c['results'].get_ordered_facets(facet):
+                        <a href="${c['url_manager'].get_add_date_url(facet, lower, upper)}">
+    %                   if upper != -1:
+                            ${c['config'].get_value_display(facet, str(lower))}
+        %                   if c['config'].display_upper(facet, str(lower), str(upper)):
+                                - 
+                                ${c['config'].get_value_display(facet, str(upper))} 
+        %                   endif
+                            (${count})
+    %                   else:
+                            ${c['config'].get_value_display(facet, str(lower))}+ (${count})
+    %                   endif
+                        </a><br/>
+    %               endfor
+    %           endif 
+    
     %       else:
     
     %           for value, count in c['results'].get_ordered_facets(facet):
 <p>
     Results per page: 
 % for rpp in c['config'].results_per_page_options:
+%   if rpp == c['results'].page_size():
+    <strong>${rpp}</strong>
+%   else:
     <a href="${c['url_manager'].get_rpp_url(rpp)}">${rpp}</a>
+%   endif
 % endfor
 
-    Sort by: 
-% for sortby in c['config'].sort_fields:
-    <a href="${c['url_manager'].get_sort_url(sortby, 'asc')}">^</a>${sortby}<a href="${c['url_manager'].get_sort_url(sortby, 'desc')}">v</a>
+<br/>
+% if len(c['results'].current_sort_fields()) > 0:
+    Sorting by: 
+% for sortby, direction in c['results'].current_sort_order():
+    <strong>
+    ${c['config'].sort_fields[sortby].get("display", sortby)}
+    % if direction == 'asc':
+        (^ <a href="${c['url_manager'].get_sort_url(sortby, 'desc')}">v</a>)
+    % endif
+    % if direction == 'desc':
+        (<a href="${c['url_manager'].get_sort_url(sortby, 'asc')}">^</a> v)
+    % endif
+    <a href="${c['url_manager'].get_unsort_url(sortby)}">x</a>
+    </strong>
+    then
 % endfor
+-
+% endif
+
+% if len([f for f in c['config'].sort_fields.keys() if f not in c['results'].current_sort_fields()]) > 0:
+% if len(c['results'].current_sort_fields()) == 0:
+Sort by:
+% endif
+
+% for sortby in c['config'].sort_fields.keys():
+%   if sortby not in c['results'].current_sort_fields():
+    ${c['config'].sort_fields[sortby].get("display", sortby)} (<a href="${c['url_manager'].get_sort_url(sortby, 'asc')}">^</a> <a href="${c['url_manager'].get_sort_url(sortby, 'desc')}">v</a>)
+%   endif
+% endfor
+% endif
 
 </p>
 

File templates/table-view.mako

     <tr>
 %   for field in c['config'].display_field_order:
         <td>
-%        if hasattr(result.get(field), "append"):
-            ${", ".join([str(val) for val in result.get(field)])}
-%        else:
-            ${str(result.get(field))}
-%        endif
+            ${c['results'].get_str(result, field)}
         </td>
 %   endfor
     </tr>