Author: ahorincar
Date: Wed Aug 13 16:08:04 2014
New Revision: 1617747

URL: http://svn.apache.org/r1617747
Log:
Finished More Like This feature, formatted code, refactored code

Added:
    bloodhound/branches/bep_0014_solr/bloodhound_solr/README
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/solr_backend.py
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/__init__.py
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/backend.py
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/schema.py
    
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/search_resources/
    
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/search_resources/__init__.py
    
bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/templates/bh_more_like_this.html
Removed:
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/backend.py
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/schemadoc/
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/web_ui.py
Modified:
    bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/api.py
    
bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/templates/bhsearch.html
    bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/web_ui.py
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/__init__.py
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/admin.py
    bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/schema.py
    
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/__init__.py
    
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/changeset_search.py
    
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/milestone_search.py
    
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/ticket_search.py
    
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/wiki_search.py
    bloodhound/branches/bep_0014_solr/bloodhound_solr/setup.py
    bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/theme.py

Modified: bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/api.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/api.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/api.py 
(original)
+++ bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/api.py Wed Aug 
13 16:08:04 2014
@@ -319,13 +319,20 @@ class BloodhoundSearchApi(Component):
         for query_processor in self.query_processors:
             query_processor.query_pre_process(query_parameters, context)
 
-        query_result = self.backend.query(**query_parameters)
+        # Compatibility with both Solr and Whoosh backends.
+        mlt = None
+        hexdigests = None
+
+        if self.backend.__class__.__name__ == 'SolrBackend':
+            query_result, mlt, hexdigests = 
self.backend.query(**query_parameters)
+        else:
+            query_result = self.backend.query(**query_parameters)
 
         for post_processor in self.result_post_processors:
             post_processor.post_process(query_result)
 
         query_result.debug["api_parameters"] = query_parameters
-        return query_result
+        return query_result, mlt, hexdigests
 
     def start_operation(self):
         return self.backend.start_operation()

Modified: 
bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/templates/bhsearch.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/templates/bhsearch.html?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- 
bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/templates/bhsearch.html
 (original)
+++ 
bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/templates/bhsearch.html
 Wed Aug 13 16:08:04 2014
@@ -166,6 +166,10 @@
                       <py:if test="result.author"><span class="author" 
i18n:msg="author">By ${format_author(result.author)}</span> &mdash;</py:if>
                       <span class="date">${result.date}</span>
                     </dd>
+                    <!-- Include the template that displays More Like This 
results for each result (only for when Solr backend is being used, not Whoosh). 
-->
+                    <dd py:if="mlt and hexdigests">
+                      <xi:include py:with="doc = result; doc_mlt = 
mlt[result.unique_id]; hexdigest = hexdigests[result.unique_id];" 
href="bh_more_like_this.html" />
+                    </dd>
                   </py:for>
                 </dl>
               </py:if>

Modified: bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/web_ui.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/web_ui.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/web_ui.py 
(original)
+++ bloodhound/branches/bep_0014_solr/bloodhound_search/bhsearch/web_ui.py Wed 
Aug 13 16:08:04 2014
@@ -338,7 +338,7 @@ class BloodhoundSearchModule(Component):
         # compatibility with legacy search
         req.search_query = request_context.parameters.query
 
-        query_result = BloodhoundSearchApi(self.env).query(
+        query_result, mlt, hexdigests = BloodhoundSearchApi(self.env).query(
             request_context.parameters.query,
             pagenum=request_context.page,
             pagelen=request_context.pagelen,
@@ -350,6 +350,11 @@ class BloodhoundSearchModule(Component):
             context=request_context,
         )
 
+        # Needed for showing More Like This results in Genshi
+        # templates.
+        request_context.data['mlt'] = mlt
+        request_context.data['hexdigests'] = hexdigests
+
         request_context.process_results(query_result)
         return self._return_data(req, request_context.data)
 

Added: bloodhound/branches/bep_0014_solr/bloodhound_solr/README
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/README?rev=1617747&view=auto
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_solr/README (added)
+++ bloodhound/branches/bep_0014_solr/bloodhound_solr/README Wed Aug 13 
16:08:04 2014
@@ -0,0 +1,29 @@
+= Plugin to add Apache Solr as a search alternative for Apache Bloodhound =
+
+== Description ==
+
+The plugin enhances Apache Bloodhound with Apache Solr functionality, namely
+it allows using Apache Solr as a search alternative.
+
+== Dependencies ==
+
+This plugin depends on the following components to be installed:
+
+  - [http:trac.edgewall.org Trac]  ,,Since version ''' 1.0 ''',, .
+  - [http://lucene.apache.org/solr/index.html Apache Solr] ,,Version ''' 4.7.2 
''',, .
+  - [http://lxml.de/ Lxml]
+  - [https://github.com/tow/sunburnt Sunburnt] ,,Since version '''0.6''',, .
+  - [https://code.google.com/p/httplib2/ Httplib2]
+
+
+== Latest Version ==
+
+
+== Installation ==
+
+
+== Licensing ==
+
+
+== Contacts ==
+

Modified: bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/__init__.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/__init__.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/__init__.py 
(original)
+++ bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/__init__.py Wed 
Aug 13 16:08:04 2014
@@ -0,0 +1,18 @@
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.

Modified: bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/admin.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/admin.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/admin.py (original)
+++ bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/admin.py Wed Aug 
13 16:08:04 2014
@@ -1,6 +1,25 @@
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
 from trac.core import Component, implements
-from bhsolr.schema import SolrSchema
 from trac.admin import IAdminCommandProvider
+from bhsolr.schema import SolrSchema
 
 class BloodhoundSolrAdmin(Component):
 

Modified: bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/schema.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/schema.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/schema.py 
(original)
+++ bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/schema.py Wed Aug 
13 16:08:04 2014
@@ -1,169 +1,198 @@
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+import os
+
 from lxml import etree
-from bhsearch.whoosh_backend import WhooshBackend
+
 from trac.core import Component, implements, TracError
-import os
+from bhsearch.whoosh_backend import WhooshBackend
+from bhsolr.solr_backend import SolrBackend
 
 class SolrSchema(Component):
-  instance = None
 
-  REQUIRED_FIELDS = {"id": True,
-                     "unique_id": True,
-                     "type": True}
-
-  FIELDS_TYPE_DICT = {"ID": "string",
-                      "DATETIME": "date",
-                      "KEYWORD": "string",
-                      "TEXT": "text_general"
-                      }
-
-  def __init__(self):
-    self.schema = WhooshBackend.SCHEMA
-    self.schema_element = etree.Element("schema")
-    self.schema_element.set("name", "Bloodhound Solr Schema")
-    self.schema_element.set("version", "1")
-
-    self.path = None
-    self.fields_element = etree.SubElement(self.schema_element, "fields")
-    self.unique_key_element = etree.SubElement(self.schema_element, 
"uniqueKey")
-    self.unique_key_element.text = "unique_id"
-
-    version_field = etree.SubElement(self.fields_element, "field")
-    version_field.set("name", "_version_")
-    version_field.set("type", "long")
-    version_field.set("indexed", "true")
-    version_field.set("stored", "true")
-
-    root_field = etree.SubElement(self.fields_element, "field")
-    root_field.set("name", "_root_")
-    root_field.set("type", "string")
-    root_field.set("indexed", "true")
-    root_field.set("stored", "false")
-
-    stored_name = etree.SubElement(self.fields_element, "field")
-    stored_name.set("name", "_stored_name")
-    stored_name.set("type", "string")
-    stored_name.set("indexed", "true")
-    stored_name.set("stored", "true")
-    stored_name.set("required", "false")
-    stored_name.set("multivalued", "false")
-
-  # @classmethod
-  # def getInstance(self, env):
-  #   if not self.instance:
-  #     self.instance = SolrSchema(env)
-  #   return self.instance
-
-  def generate_schema(self, path=None):
-    if not path:
-      path = os.getcwd()
-
-    self.add_all_fields()
-    self.add_type_definitions()
-    doc = etree.ElementTree(self.schema_element)
-
-    self.path = os.path.join(path, 'schema.xml')
-
-    out_file = open(os.path.join(path, 'schema.xml'), 'w')
-    doc.write(out_file, xml_declaration=True, encoding='UTF-8', 
pretty_print=True)
-    out_file.close()
-
-  def add_field(self, field_name, name_attr, type_attr, indexed_attr, 
stored_attr, required_attr, multivalued_attr):
-    field = etree.SubElement(self.fields_element, field_name)
-    field.set("name", name_attr)
-    field.set("type", type_attr)
-    field.set("indexed", indexed_attr)
-    field.set("stored", stored_attr)
-    field.set("required", required_attr)
-    field.set("multivalued", multivalued_attr)
-
-  def add_all_fields(self):
-    for (field_name, field_attrs) in self.schema.items():
-      type_attr = 
SolrSchema.FIELDS_TYPE_DICT[str(field_attrs.__class__.__name__)]
-      indexed_attr = str(field_attrs.indexed).lower()
-      stored_attr = str(field_attrs.stored).lower()
-      if field_name in SolrSchema.REQUIRED_FIELDS:
-        required_attr = "true"
-      else:
-        required_attr = "false"
-
-      self.add_field("field", field_name, type_attr, indexed_attr, 
stored_attr, required_attr, "false")
-
-
-  def add_type_definitions(self):
-    self.types_element = etree.SubElement(self.schema_element, "types")
-    self._add_string_type_definition()
-    self._add_text_general_type_definition()
-    self._add_date_type_definition()
-    self._add_long_type_definition()
-    self._add_lowercase_type_definition()
-
-
-  def _add_string_type_definition(self):
-    field_type = etree.SubElement(self.types_element, "fieldType")
-    field_type.set("name", "string")
-    field_type.set("class", "solr.StrField")
-    field_type.set("sortMissingLast", "true")
-
-
-  def _add_text_general_type_definition(self):
-    field_type = etree.SubElement(self.types_element, "fieldType")
-    field_type.set("name", "text_general")
-    field_type.set("class", "solr.TextField")
-    field_type.set("positionIncrementGap", "100")
-
-    analyzer_index = etree.SubElement(field_type, "analyzer")
-    analyzer_index.set("type", "index")
-
-    tokenizer_index = etree.SubElement(analyzer_index, "tokenizer")
-    tokenizer_index.set("class", "solr.StandardTokenizerFactory")
-    filter1 = etree.SubElement(analyzer_index, "filter")
-    filter1.set("class", "solr.StopFilterFactory")
-    filter1.set("ignoreCase", "true")
-    filter1.set("words", "stopwords.txt")
-
-    filter2 = etree.SubElement(analyzer_index, "filter")
-    filter2.set("class", "solr.LowerCaseFilterFactory")
-
-    analyzer_query = etree.SubElement(field_type, "analyzer")
-    analyzer_query.set("type", "query")
-    tokenizer_query = etree.SubElement(analyzer_query, "tokenizer")
-    tokenizer_query.set("class", "solr.StandardTokenizerFactory")
-    filter3 = etree.SubElement(analyzer_query, "filter")
-    filter3.set("class", "solr.StopFilterFactory")
-    filter3.set("ignoreCase", "true")
-    filter3.set("words", "stopwords.txt")
-
-    filter4 = etree.SubElement(analyzer_query, "filter")
-    filter4.set("class", "solr.SynonymFilterFactory")
-    filter4.set("synonyms", "synonyms.txt")
-    filter4.set("ignoreCase", "true")
-    filter4.set("expand", "true")
-
-    filter5 = etree.SubElement(analyzer_query, "filter")
-    filter5.set("class", "solr.LowerCaseFilterFactory")
-
-  def _add_date_type_definition(self):
-    field_type = etree.SubElement(self.types_element, "fieldType")
-    field_type.set("name", "date")
-    field_type.set("class", "solr.TrieDateField")
-    field_type.set("precisionStep", "0")
-    field_type.set("positionIncrementGap", "0")
-
-  def _add_long_type_definition(self):
-    field_type = etree.SubElement(self.types_element, "fieldType")
-    field_type.set("name", "long")
-    field_type.set("class", "solr.TrieLongField")
-    field_type.set("precisionStep", "0")
-    field_type.set("positionIncrementGap", "0")
-
-  def _add_lowercase_type_definition(self):
-    field_type = etree.SubElement(self.types_element, "fieldType")
-    field_type.set("name", "lowercase")
-    field_type.set("class", "solr.TextField")
-    field_type.set("positionIncrementGap", "100")
-
-    analyzer = etree.SubElement(field_type, "analyzer")
-    tokenizer = etree.SubElement(analyzer, "tokenizer")
-    tokenizer.set("class", "solr.KeywordTokenizerFactory")
-    filter_lowercase = etree.SubElement(analyzer, "filter")
-    filter_lowercase.set("class", "solr.LowerCaseFilterFactory")
+    REQUIRED_FIELDS = {
+            "id": True,
+            "unique_id": True,
+            "type": True
+            }
+
+    FIELDS_TYPE_DICT = {
+        "ID": "string",
+        "DATETIME": "date",
+        "KEYWORD": "string",
+        "TEXT": "text_general"
+        }
+
+    def __init__(self):
+        self.path = None
+        self.schema = WhooshBackend.SCHEMA
+        self.schema_element = etree.Element("schema")
+        self.schema_element.set("name", "Bloodhound Solr Schema")
+        self.schema_element.set("version", "1")
+
+        self.fields_element = etree.SubElement(self.schema_element, "fields")
+
+        self.unique_key_element = etree.SubElement(self.schema_element,
+                                                   "uniqueKey")
+        self.unique_key_element.text = SolrBackend.UNIQUE_ID
+
+        version_field = etree.SubElement(self.fields_element, "field")
+        version_field.set("name", "_version_")
+        version_field.set("type", "long")
+        version_field.set("indexed", "true")
+        version_field.set("stored", "true")
+        version_field.set("multiValued", "false")
+
+        root_field = etree.SubElement(self.fields_element, "field")
+        root_field.set("name", "_root_")
+        root_field.set("type", "string")
+        root_field.set("indexed", "true")
+        root_field.set("stored", "false")
+
+        stored_name = etree.SubElement(self.fields_element, "field")
+        stored_name.set("name", "_stored_name")
+        stored_name.set("type", "string")
+        stored_name.set("indexed", "true")
+        stored_name.set("stored", "true")
+        stored_name.set("required", "false")
+        stored_name.set("multiValued", "false")
+
+    def generate_schema(self, path=None):
+        if not path:
+          path = os.getcwd()
+        self.path = os.path.join(path, 'schema.xml')
+
+        self.add_all_fields()
+        self.add_type_definitions()
+
+        doc = etree.ElementTree(self.schema_element)
+        out_file = open(os.path.join(path, 'schema.xml'), 'w')
+        doc.write(out_file, xml_declaration=True, encoding='UTF-8',
+                  pretty_print=True)
+        out_file.close()
+
+    def add_field(
+                self, field_name, name_attr, type_attr, indexed_attr,
+                stored_attr, required_attr, multivalued_attr):
+        field = etree.SubElement(self.fields_element, field_name)
+        field.set("name", name_attr)
+        field.set("type", type_attr)
+        field.set("indexed", indexed_attr)
+        field.set("stored", stored_attr)
+        field.set("required", required_attr)
+        field.set("multiValued", multivalued_attr)
+
+    def add_all_fields(self):
+        for (field_name, field_attrs) in self.schema.items():
+            class_name = str(field_attrs.__class__.__name__)
+            type_attr = self.FIELDS_TYPE_DICT[class_name]
+            indexed_attr = str(field_attrs.indexed).lower()
+            stored_attr = str(field_attrs.stored).lower()
+
+            if field_name in self.REQUIRED_FIELDS:
+                required_attr = "true"
+            else:
+                required_attr = "false"
+
+            self.add_field("field", field_name, type_attr, indexed_attr,
+                           stored_attr, required_attr, "false")
+
+    def add_type_definitions(self):
+        self.types_element = etree.SubElement(self.schema_element, "types")
+        self._add_string_type_definition()
+        self._add_text_general_type_definition()
+        self._add_date_type_definition()
+        self._add_long_type_definition()
+        self._add_lowercase_type_definition()
+
+    def _add_string_type_definition(self):
+        field_type = etree.SubElement(self.types_element, "fieldType")
+        field_type.set("name", "string")
+        field_type.set("class", "solr.StrField")
+        field_type.set("sortMissingLast", "true")
+
+    def _add_text_general_type_definition(self):
+        field_type = etree.SubElement(self.types_element, "fieldType")
+        field_type.set("name", "text_general")
+        field_type.set("class", "solr.TextField")
+        field_type.set("positionIncrementGap", "100")
+
+        analyzer_index = etree.SubElement(field_type, "analyzer")
+        analyzer_index.set("type", "index")
+
+        tokenizer_index = etree.SubElement(analyzer_index, "tokenizer")
+        tokenizer_index.set("class", "solr.StandardTokenizerFactory")
+
+        analyzer_index_filter_s = etree.SubElement(analyzer_index, "filter")
+        analyzer_index_filter_s.set("class", "solr.StopFilterFactory")
+        analyzer_index_filter_s.set("ignoreCase", "true")
+        analyzer_index_filter_s.set("words", "stopwords.txt")
+
+        analyzer_index_filter_l = etree.SubElement(analyzer_index, "filter")
+        analyzer_index_filter_l.set("class", "solr.LowerCaseFilterFactory")
+
+        analyzer_query = etree.SubElement(field_type, "analyzer")
+        analyzer_query.set("type", "query")
+
+        tokenizer_query = etree.SubElement(analyzer_query, "tokenizer")
+        tokenizer_query.set("class", "solr.StandardTokenizerFactory")
+
+        analyzer_query_filter_s = etree.SubElement(analyzer_query, "filter")
+        analyzer_query_filter_s.set("class", "solr.StopFilterFactory")
+        analyzer_query_filter_s.set("ignoreCase", "true")
+        analyzer_query_filter_s.set("words", "stopwords.txt")
+
+        analyzer_query_filter_syn = etree.SubElement(analyzer_query, "filter")
+        analyzer_query_filter_syn.set("class", "solr.SynonymFilterFactory")
+        analyzer_query_filter_syn.set("synonyms", "synonyms.txt")
+        analyzer_query_filter_syn.set("ignoreCase", "true")
+        analyzer_query_filter_syn.set("expand", "true")
+
+        analyzer_query_filter_l = etree.SubElement(analyzer_query, "filter")
+        analyzer_query_filter_l.set("class", "solr.LowerCaseFilterFactory")
+
+    def _add_date_type_definition(self):
+        field_type = etree.SubElement(self.types_element, "fieldType")
+        field_type.set("name", "date")
+        field_type.set("class", "solr.TrieDateField")
+        field_type.set("precisionStep", "0")
+        field_type.set("positionIncrementGap", "0")
+
+    def _add_long_type_definition(self):
+        field_type = etree.SubElement(self.types_element, "fieldType")
+        field_type.set("name", "long")
+        field_type.set("class", "solr.TrieLongField")
+        field_type.set("precisionStep", "0")
+        field_type.set("positionIncrementGap", "0")
+
+    def _add_lowercase_type_definition(self):
+        field_type = etree.SubElement(self.types_element, "fieldType")
+        field_type.set("name", "lowercase")
+        field_type.set("class", "solr.TextField")
+        field_type.set("positionIncrementGap", "100")
+
+        analyzer = etree.SubElement(field_type, "analyzer")
+
+        tokenizer = etree.SubElement(analyzer, "tokenizer")
+        tokenizer.set("class", "solr.KeywordTokenizerFactory")
+
+        analyzer_filter_l = etree.SubElement(analyzer, "filter")
+        analyzer_filter_l.set("class", "solr.LowerCaseFilterFactory")
+

Modified: 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/__init__.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/__init__.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/__init__.py
 (original)
+++ 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/__init__.py
 Wed Aug 13 16:08:04 2014
@@ -0,0 +1,18 @@
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.

Modified: 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/changeset_search.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/changeset_search.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/changeset_search.py
 (original)
+++ 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/changeset_search.py
 Wed Aug 13 16:08:04 2014
@@ -1,17 +1,36 @@
-from bhsearch.search_resources.base import BaseIndexer
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
 from trac.versioncontrol.api import RepositoryManager
+from bhsearch.search_resources.base import BaseIndexer
 from bhsearch.search_resources.changeset_search import ChangesetIndexer
 
 class ChangesetSearchModel(BaseIndexer):
 
-  def get_entries_for_index(self):
-    repository_manager = RepositoryManager(self.env)
-    for repository in repository_manager.get_real_repositories():
-      rev = repository.oldest_rev
-      stop = repository.youngest_rev
-      while True:
-        changeset = repository.get_changeset(rev)
-        yield ChangesetIndexer(self.env).build_doc(changeset)
-        if rev == stop:
-          break
-        rev = repository.next_rev(rev)
+    def get_entries_for_index(self):
+        repository_manager = RepositoryManager(self.env)
+        for repository in repository_manager.get_real_repositories():
+            rev = repository.oldest_rev
+            stop = repository.youngest_rev
+            while True:
+                changeset = repository.get_changeset(rev)
+                yield ChangesetIndexer(self.env).build_doc(changeset)
+                if rev == stop:
+                    break
+                rev = repository.next_rev(rev)

Modified: 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/milestone_search.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/milestone_search.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/milestone_search.py
 (original)
+++ 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/milestone_search.py
 Wed Aug 13 16:08:04 2014
@@ -1,9 +1,28 @@
-from bhsearch.search_resources.base import BaseIndexer
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
 from trac.ticket import Milestone
+from bhsearch.search_resources.base import BaseIndexer
 from bhsearch.search_resources.milestone_search import MilestoneIndexer
 
 class MilestoneSearchModel(BaseIndexer):
 
-  def get_entries_for_index(self):
-    for milestone in Milestone.select(self.env, include_completed=True):
-      yield MilestoneIndexer(self.env).build_doc(milestone)
+    def get_entries_for_index(self):
+        for milestone in Milestone.select(self.env, include_completed=True):
+            yield MilestoneIndexer(self.env).build_doc(milestone)

Modified: 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/ticket_search.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/ticket_search.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/ticket_search.py
 (original)
+++ 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/ticket_search.py
 Wed Aug 13 16:08:04 2014
@@ -1,27 +1,46 @@
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
 from trac.ticket.model import Ticket
-from bhsearch.search_resources.ticket_search import TicketIndexer
 from trac.core import Component, implements, TracError
+from bhsearch.search_resources.ticket_search import TicketIndexer
 from bhsearch.search_resources.base import BaseIndexer
 
 class TicketSearchModel(BaseIndexer):
 
-  def _fetch_tickets(self,  **kwargs):
-    for ticket_id in self._fetch_ids(**kwargs):
-      yield Ticket(self.env, ticket_id)
-
-  def _fetch_ids(self, **kwargs):
-    sql = "SELECT id FROM ticket"
-    args = []
-    conditions = []
-    for key, value in kwargs.iteritems():
-      args.append(value)
-      conditions.append(key + "=%s")
-    if conditions:
-      sql = sql + " WHERE " + " AND ".join(conditions)
-    for row in self.env.db_query(sql, args):
-      yield int(row[0])
-
-  def get_entries_for_index(self):
-    for ticket in self._fetch_tickets():
-      yield TicketIndexer(self.env).build_doc(ticket)
+    def _fetch_tickets(self,  **kwargs):
+        for ticket_id in self._fetch_ids(**kwargs):
+            yield Ticket(self.env, ticket_id)
+
+    def _fetch_ids(self, **kwargs):
+        sql = "SELECT id FROM ticket"
+        args = []
+        conditions = []
+        for key, value in kwargs.iteritems():
+            args.append(value)
+            conditions.append(key + "=%s")
+        if conditions:
+            sql = sql + " WHERE " + " AND ".join(conditions)
+        for row in self.env.db_query(sql, args):
+            yield int(row[0])
+
+    def get_entries_for_index(self):
+        for ticket in self._fetch_tickets():
+            yield TicketIndexer(self.env).build_doc(ticket)
 

Modified: 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/wiki_search.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/wiki_search.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/wiki_search.py
 (original)
+++ 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/search_resources/wiki_search.py
 Wed Aug 13 16:08:04 2014
@@ -1,11 +1,30 @@
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
 from trac.wiki import WikiSystem, WikiPage
 from bhsearch.search_resources.wiki_search import WikiIndexer
 from bhsearch.search_resources.base import BaseIndexer
 
 class WikiSearchModel(BaseIndexer):
 
-  def get_entries_for_index(self):
-    page_names = WikiSystem(self.env).get_pages()
-    for page_name in page_names:
-      page = WikiPage(self.env, page_name)
-      yield WikiIndexer(self.env).build_doc(page)
+    def get_entries_for_index(self):
+        page_names = WikiSystem(self.env).get_pages()
+        for page_name in page_names:
+            page = WikiPage(self.env, page_name)
+            yield WikiIndexer(self.env).build_doc(page)

Added: bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/solr_backend.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/solr_backend.py?rev=1617747&view=auto
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/solr_backend.py 
(added)
+++ bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/solr_backend.py 
Wed Aug 13 16:08:04 2014
@@ -0,0 +1,276 @@
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+import re
+import hashlib
+
+from math import ceil
+from datetime import datetime
+from contextlib import contextmanager
+from sunburnt import SolrInterface
+
+from bhsearch import BHSEARCH_CONFIG_SECTION
+from bhsearch.api import ISearchBackend, SCORE, QueryResult
+from bhsearch.query_parser import DefaultQueryParser
+from bhsearch.search_resources.ticket_search import TicketIndexer
+from multiproduct.env import ProductEnvironment
+from trac.core import Component, implements, TracError
+from trac.config import Option
+from trac.ticket.model import Ticket
+from trac.ticket.api import TicketSystem
+from trac.util.datefmt import utc
+
+
+class SolrBackend(Component):
+    implements(ISearchBackend)
+
+    UNIQUE_ID = "unique_id"
+
+    HIGHLIGHTABLE_FIELDS = {
+        "unique_id" : True,
+        "id" : True,
+        "type" : True,
+        "product" : True,
+        "milestone" : True,
+        "author" : True,
+        "component" : True,
+        "status" : True,
+        "resolution" : True,
+        "keywords" : True,
+        "summary" : True,
+        "content" : True,
+        "changes" : True,
+        "owner" : True,
+        "repository" : True,
+        "revision" : True,
+        "message" : True,
+        "name" : True
+        }
+
+    server_url = Option(
+            BHSEARCH_CONFIG_SECTION,
+            'solr_server_url',
+            doc="""Url of the server running Solr instance.""",
+            doc_domain='bhsearch')
+
+    def __init__(self):
+        self.solr_interface = SolrInterface(str(self.server_url))
+
+    def add_doc(self, doc, operation_context=None):
+        self._reformat_doc(doc)
+        doc[self.UNIQUE_ID] = self._create_unique_id(doc.get("product", ''),
+                                                doc["type"], doc["id"])
+        self.solr_interface.add(doc)
+        self.solr_interface.commit()
+
+    def delete_doc(product, doc_type, doc_id, operation_context=None):
+        unique_id = self._create_unique_id(product, doc_type, doc_id)
+        self.solr_interface.delete(unique_id)
+
+    def optimize(self):
+        self.solr_interface.optimize()
+
+    def query(
+            self, query, query_string, sort = None, fields = None,
+            filter = None, facets = None, pagenum = 1, pagelen = 20,
+            highlight = False, highlight_fields = None, context = None):
+
+        if not query_string:
+            query_string = "*.*"
+
+        final_query_chain = self._create_query_chain(query, query_string)
+        solr_query = self.solr_interface.query(final_query_chain)
+        faceted_solr_query = solr_query.facet_by(facets)
+        highlighted_solr_query = faceted_solr_query.highlight(
+                                    self.HIGHLIGHTABLE_FIELDS)
+
+        start = 0 if pagenum == 1 else pagelen * pagenum
+        paginated_solr_query = highlighted_solr_query.paginate(
+                            start=start, rows=pagelen)
+        results = paginated_solr_query.execute()
+
+        mlt, hexdigests = self.query_more_like_this(paginated_solr_query,
+                                                    fields="type", mindf=1,
+                                                    mintf=1)
+
+        query_result = self._create_query_result(highlighted_solr_query,
+                                                 results, fields, pagenum,
+                                                 pagelen)
+        return query_result, mlt, hexdigests
+
+    def query_more_like_this(self, query_chain, **kwargs):
+        mlt_results = query_chain.mlt(**kwargs).execute().more_like_these
+        mlt_dict = {}
+        hexdigests = {}
+
+        for doc, results in mlt_results.iteritems():
+            hexdigest = hashlib.md5(doc).hexdigest()
+            hexdigests[doc] = hexdigest
+
+            for mlt_doc in results.docs:
+                if doc not in mlt_dict:
+                    mlt_dict[doc] = [self._process_doc(mlt_doc)]
+                else:
+                    mlt_dict[doc].append(self._process_doc(mlt_doc))
+
+        return mlt_dict, hexdigests
+
+    def _process_doc(self, doc):
+        ui_doc = dict(doc)
+
+        if doc.get('product'):
+            env = ProductEnvironment(self.env, doc['product'])
+            product_href = ProductEnvironment.resolve_href(env, self.env)
+            ui_doc["href"] = product_href(doc['type'], doc['id'])
+        else:
+            ui_doc["href"] = self.env.href(doc['type'], doc['id'])
+
+        ui_doc['title'] = str(doc['type'] + ": " + doc['_stored_name']).title()
+
+        return ui_doc
+
+    def _create_query_result(
+                        self, query, results, fields, pagenum, pagelen):
+        total_num, total_page_count, page_num, offset = \
+                    self._prepare_query_result_attributes(query, results,
+                                                          pagenum, pagelen)
+
+        query_results = QueryResult()
+        query_results.hits = total_num
+        query_results.total_page_count = total_page_count
+        query_results.page_number = page_num
+        query_results.offset = offset
+
+        docs = []
+        highlighting = []
+
+        for retrieved_record in results:
+            result_doc = self._process_record(fields, retrieved_record)
+            docs.append(result_doc)
+
+            result_highlights = dict(retrieved_record['solr_highlights'])
+
+            highlighting.append(result_highlights)
+            query_results.docs = docs
+            query_results.highlighting = highlighting
+
+        return query_results
+
+    def _create_query_chain(self, query, query_string):
+        matches = re.findall(re.compile(r'([\w\*]+)'), query_string)
+        tokens = set([match for match in matches])
+
+        final_query_chain = None
+        for token in tokens:
+            token_query_chain = self._search_fields_for_token(token)
+            if final_query_chain is None:
+                final_query_chain = token_query_chain
+            else:
+                final_query_chain |= token_query_chain
+
+        return final_query_chain
+
+    def _process_record(self, fields, retrieved_record):
+        result_doc = dict()
+        if fields:
+            for field in fields:
+                if field in retrieved_record:
+                    result_doc[field] = retrieved_record[field]
+        else:
+            for key, value in retrieved_record.items():
+                result_doc[key] = value
+
+        for key, value in result_doc.iteritems():
+            result_doc[key] = self._from_whoosh_format(value)
+
+        return result_doc
+
+    def _from_whoosh_format(self, value):
+        if isinstance(value, datetime):
+            value = utc.localize(value)
+        return value
+
+    def _prepare_query_result_attributes(
+                                    self, query, results, pagenum, pagelen):
+        results_total_num = query.execute().result.numFound
+        total_page_count = int(ceil(results_total_num / pagelen))
+        pagenum = min(total_page_count, pagenum)
+
+        offset = (pagenum-1) * pagelen
+        if (offset+pagelen) > results_total_num:
+            pagelen = results_total_num - offset
+
+        return results_total_num, total_page_count, pagenum, offset
+
+    def is_index_outdated(self):
+        return False
+
+    def recreate_index(self):
+        return True
+
+    @contextmanager
+    def start_operation(self):
+        yield
+
+    def _search_fields_for_token(self, token):
+        q_chain = None
+        field_boosts = DefaultQueryParser(self.env).field_boosts
+
+        for field, boost in field_boosts.iteritems():
+            if field != 'query_suggestion_basket' and field != 'relations':
+                field_token_dict = {field: token}
+                if q_chain is None:
+                    q_chain = self.solr_interface.Q(**field_token_dict)**boost
+                else:
+                    q_chain |= self.solr_interface.Q(**field_token_dict)**boost
+
+        return q_chain
+
+    def _reformat_doc(self, doc):
+        for key, value in doc.items():
+            if key is None:
+                del doc[None]
+            elif value is None:
+                del doc[key]
+            elif isinstance(value, basestring) and value == "":
+                del doc[key]
+            else:
+                doc[key] = self._to_whoosh_format(value)
+
+    def _to_whoosh_format(self, value):
+        if isinstance(value, basestring):
+            value = unicode(value)
+        elif isinstance(value, datetime):
+            value = self._convert_date_to_tz_naive_utc(value)
+        return value
+
+    def _convert_date_to_tz_naive_utc(self, value):
+        if value.tzinfo:
+            utc_time = value.astimezone(utc)
+            value = utc_time.replace(tzinfo=None)
+        return value
+
+    def _create_unique_id(self, product, doc_type, doc_id):
+        if product:
+            return u"%s:%s:%s" % (product, doc_type, doc_id)
+        else:
+            return u"%s:%s" % (doc_type, doc_id)
+
+
+

Added: 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/__init__.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/__init__.py?rev=1617747&view=auto
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/__init__.py 
(added)
+++ bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/__init__.py 
Wed Aug 13 16:08:04 2014
@@ -0,0 +1,18 @@
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
+from bhsolr.tests import (
+    backend
+)
+
+
+def suite():
+    test_suite = unittest.TestSuite()
+    return test_suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')
+else:
+    test_suite = suite()

Added: bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/backend.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/backend.py?rev=1617747&view=auto
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/backend.py 
(added)
+++ bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/backend.py 
Wed Aug 13 16:08:04 2014
@@ -0,0 +1,21 @@
+import unittest
+
+from trac.test import EnvironmentStub, Mock, MockPerm
+from trac.util.datefmt import utc
+from trac.web.chrome import Chrome
+from bhsearch.tests.base import BaseBloodhoundSearchTest
+
+class SolrBackendTestCase(BaseBloodhoundSearchTest):
+  def setUp(self):
+    super(SolrBackendTestCase, self).setUp()
+    self.solr_backend = SolrBackend(self.env)
+    # self.parser = DefaultQueryParser(self.env)
+
+def suite():
+  suite = unittest.TestSuite()
+  suite.addTest(unittest.makeSuite(SolrBackendTestCase))
+  return suite
+
+
+if __name__ == '__main__':
+  unittest.main(defaultTest='suite')

Added: bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/schema.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/schema.py?rev=1617747&view=auto
==============================================================================
    (empty)

Added: 
bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/search_resources/__init__.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/bhsolr/tests/search_resources/__init__.py?rev=1617747&view=auto
==============================================================================
    (empty)

Modified: bloodhound/branches/bep_0014_solr/bloodhound_solr/setup.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_solr/setup.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_solr/setup.py (original)
+++ bloodhound/branches/bep_0014_solr/bloodhound_solr/setup.py Wed Aug 13 
16:08:04 2014
@@ -1,34 +1,59 @@
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
 from setuptools import setup, find_packages
 
-PKG_INFO = {'bhsolr': ['schemadoc/*.xml'],
-            'bhsolr.search_resources' : [],
-            }
 
+PKG_INFO = {
+    'bhsolr': ['../README', '../TESTING_README'],
+    'bhsolr.search_resources' : [],
+    'bhsolr.tests': ['*.*'],
+    'bhsolr.tests.search_resources': ['*.*'],
+    }
 
 
 ENTRY_POINTS = {
-          'trac.plugins': [
-          'bhsolr.admin = bhsolr.admin',
-          'bhsolr.schema = bhsolr.schema',
-          'bhsolr.backend = bhsolr.backend',
-          'bhsolr.web_ui = bhsolr.web_ui',
-          'bhsolr.search_resources.ticket_search = 
bhsolr.search_resources.ticket_search',
-          'bhsolr.search_resources.milestone_search = 
bhsolr.search_resources.milestone_search',
-          'bhsolr.search_resources.changeset_search = 
bhsolr.search_resources.changeset_search',
-          'bhsolr.search_resources.wiki_search = 
bhsolr.search_resources.wiki_search'
-      ],}
+    'trac.plugins': [
+    'bhsolr.admin = bhsolr.admin',
+    'bhsolr.schema = bhsolr.schema',
+    'bhsolr.solr_backend = bhsolr.solr_backend',
+    'bhsolr.search_resources.ticket_search = \
+                                        bhsolr.search_resources.ticket_search',
+    'bhsolr.search_resources.milestone_search = \
+                                    bhsolr.search_resources.milestone_search',
+    'bhsolr.search_resources.changeset_search = \
+                                    bhsolr.search_resources.changeset_search',
+    'bhsolr.search_resources.wiki_search = bhsolr.search_resources.wiki_search'
+    ],}
+
 
 setup(
-  name = 'BloodhoundSolrPlugin',
-  version = '0.1',
-  description = "Apache Solr support for Apache(TM) Bloodhound.",
-  author = "Apache Bloodhound",
-  license = "Apache License v2",
-  url = "http://bloodhound.apache.org/";,
-  requires = ['trac', 'lxml', 'sunburnt', 'httplib2'],
-  packages = find_packages(),
-  package_data = PKG_INFO,
-  include_package_data=True,
-  entry_points = ENTRY_POINTS,
-  test_suite='bhsolr.tests.test_suite',
-)
+    name = 'BloodhoundSolrPlugin',
+    version = '0.1',
+    description = "Apache Solr support for Apache(TM) Bloodhound.",
+    author = "Apache Bloodhound",
+    license = "Apache License v2",
+    url = "http://bloodhound.apache.org/";,
+    requires = ['trac', 'lxml', 'sunburnt', 'httplib2'],
+    packages = find_packages(),
+    package_data = PKG_INFO,
+    include_package_data=True,
+    entry_points = ENTRY_POINTS,
+    test_suite='bhsolr.tests.test_suite',
+    )

Added: 
bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/templates/bh_more_like_this.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/templates/bh_more_like_this.html?rev=1617747&view=auto
==============================================================================
--- 
bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/templates/bh_more_like_this.html
 (added)
+++ 
bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/templates/bh_more_like_this.html
 Wed Aug 13 16:08:04 2014
@@ -0,0 +1,32 @@
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml";
+      xmlns:xi="http://www.w3.org/2001/XInclude";
+      xmlns:py="http://genshi.edgewall.org/";
+      xmlns:i18n="http://genshi.edgewall.org/i18n";>
+
+  <body>
+    <div class="panel-group" id="accordion${doc.unique_id}">
+      <div class="panel panel-default">
+        <div class="panel-heading">
+          <h4 class="panel-title">
+            <a class="btn" data-toggle="collapse" 
data-parent="#accordion${doc.unique_id}" href="#collapse${hexdigest}">
+              More Like This
+            </a>
+          </h4>
+        </div>
+        <div id="collapse${hexdigest}" class="panel-collapse collapse out">
+          <div class="panel-body">
+            <dl>
+              <py:for each="result in doc_mlt">
+                <dt><a href="${result.href}">${result.title}</a></dt>
+              </py:for>
+            </dl>
+          </div>
+        </div>
+      </div>
+    </div>
+
+  </body>
+</html>

Modified: bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/theme.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/theme.py?rev=1617747&r1=1617746&r2=1617747&view=diff
==============================================================================
--- bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/theme.py 
(original)
+++ bloodhound/branches/bep_0014_solr/bloodhound_theme/bhtheme/theme.py Wed Aug 
13 16:08:04 2014
@@ -353,6 +353,7 @@ class BloodhoundTheme(ThemeBase):
             req.search_query = data.get('query')
             # Context nav
             prevnext_nav(req, _('Previous'), _('Next'))
+        self._add_more_like_this(req, template, data, content_type, is_active)
         # Breadcrumbs nav
         data['resourcepath_template'] = 'bh_path_search.html'
 
@@ -451,6 +452,11 @@ class BloodhoundTheme(ThemeBase):
         if isinstance(req.perm.env, ProductEnvironment):
             data['resourcepath_template'] = 'bh_path_general.html'
 
+    def _add_more_like_this(self, req, template, data,
+                                         content_type, is_active):
+        """Adds a template for displaying More Like This results."""
+        data['resourcepath_template'] = 'bh_more_like_this.html'
+
     def _modify_product_list(self, req, template, data, content_type,
                              is_active):
         """Transform products list into media list by adding


Reply via email to