# HG changeset patch
# User dwinter
# Date 1361888527 -3600
# Node ID 834706423ac1042d43b2081e1dfa014a5fdb1897
initial
diff -r 000000000000 -r 834706423ac1 .project
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.project Tue Feb 26 15:22:07 2013 +0100
@@ -0,0 +1,17 @@
+
+
+ zopeSolr
+
+
+
+
+
+ org.python.pydev.PyDevBuilder
+
+
+
+
+
+ org.python.pydev.pythonNature
+
+
diff -r 000000000000 -r 834706423ac1 .pydevproject
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.pydevproject Tue Feb 26 15:22:07 2013 +0100
@@ -0,0 +1,8 @@
+
+
+
+/zopeSolr
+
+python 2.7
+pythonZope13
+
diff -r 000000000000 -r 834706423ac1 .settings/org.eclipse.core.resources.prefs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.settings/org.eclipse.core.resources.prefs Tue Feb 26 15:22:07 2013 +0100
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+encoding/=UTF-8
+encoding/zopeSolr.py=utf-8
diff -r 000000000000 -r 834706423ac1 __init__.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/__init__.py Tue Feb 26 15:22:07 2013 +0100
@@ -0,0 +1,15 @@
+import zopeSolr
+
+def initialize(context):
+ """initialize OSAS"""
+
+
+ context.registerClass(
+ zopeSolr.ZopeSolr,
+ constructors = (
+ zopeSolr.manage_addZopeSolrForm,
+ zopeSolr.manage_addZopeSolr
+ )
+ )
+
+
diff -r 000000000000 -r 834706423ac1 test/test.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test.py Tue Feb 26 15:22:07 2013 +0100
@@ -0,0 +1,30 @@
+'''
+Created on 11.02.2013
+
+@author: dwinter
+'''
+
+import sunburnt
+import httplib
+
+class TestSolr:
+
+ solr = None
+ def __init__(self):
+ self.solr=sunburnt.SolrInterface(url="http://127.0.0.1:8983/solr/mpiwgweb")
+
+if __name__ == '__main__':
+
+ sl = TestSolr()
+
+ #params={facet=true&facet.field=description&facet.field=main_content&start=0&q=main_content:knowledge+AND+renn+AND+description:*+AND+main_content:*&rows=10} hits=113 status=0 QTime=66
+
+ x= {"qt":"tvrh","tv":"on","fl":"main_content","tv.tf":True}
+
+ fq = sl.solr.query(urlNorm="http://127.0.0.1:18080/www_neu/en/news/features/feature25",**x).facet_by("main_content")
+ x = fq.execute()
+
+ #http://localhost:8983/solr/mpiwgweb/select?q=*%3A*&wt=xml&tv=on&qt=tvrh&fl=main_content&tv.tf=true
+ #http://localhost:8983/solr/mpiwgweb/select?q=*%3A*&wt=json&tv=on&qt=tvrh&fl=main_content&tv.tf=true
+ print x.facet_counts.facet_fields['main_content']
+
\ No newline at end of file
diff -r 000000000000 -r 834706423ac1 test/test2.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test2.py Tue Feb 26 15:22:07 2013 +0100
@@ -0,0 +1,23 @@
+import httplib2
+import xml.etree.ElementTree as ET
+
+h = httplib2.Http()
+resp, content = h.request("http://localhost:8983/solr/mpiwgweb/select?q=*%3A*&wt=xml&tv=on&qt=tvrh&fl=main_content&tv.tf=true")
+
+root = ET.fromstring(content)
+uri = "http://127.0.0.1:18080/www_neu/de/aktuelles/features/feature28"
+fieldname="main_content"
+tvs = root.findall(".//lst[@name='termVectors']/lst[@name='%s']/lst[@name='%s']/lst"%(uri,fieldname))
+print tvs
+for tv in tvs:
+ word = tv.attrib['name']
+ for f in tv.findall("./int[@name='tf']"):
+ fre = f.text
+
+ print word,fre
+
+
+
+
+
+#urlNormhttp://127.0.0.1:18080/www_neu/de/aktuelles/features/feature2812
\ No newline at end of file
diff -r 000000000000 -r 834706423ac1 zopeSolr.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/zopeSolr.py Tue Feb 26 15:22:07 2013 +0100
@@ -0,0 +1,436 @@
+# -*- coding: utf-8 -*-
+
+#Verbindet Zope mit solr. Vorraussetzung ist das Paket sunburnt, @see http://opensource.timetric.com/sunburnt/
+
+
+from OFS.SimpleItem import SimpleItem
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+import pysolr
+import os.path
+import sunburnt
+from Globals import package_home
+import httplib2
+import urllib
+import re
+import xml.etree.ElementTree as ET
+import json
+
+#Worte die nicht in der Termliste angezeigt werden sollen #TODO: make this configurable
+
+STOPLIST={'main_content':['forward','drucken','history','science','part','publications','projects',
+ 'project','new','geschichte','institute','related','boltzmannstraße','14195'],
+
+ 'title':['bd','10','11','12','18','19','20','abt','ad','di','history','geschichte','science']
+ }
+
+
+
+def zptFile(self, path, orphaned=False):
+ """returns a page template file from the product"""
+ if orphaned:
+ # unusual case
+ pt=PageTemplateFile(os.path.join(package_home(globals()), path))
+ else:
+
+ pt=PageTemplateFile(os.path.join(package_home(globals()), path)).__of__(self)
+ return pt
+
+
+
+class ZopeSolr(SimpleItem):
+
+
+ meta_type="ZopeSolr"
+
+ manage_options= ({'label':'Main Config','action': 'changeMain'},) + SimpleItem.manage_options
+
+ def __init__(self,id,title,solrURL):
+ self.id=id
+ self.title=title
+ self.solrURL=solrURL #URL einer solr instance bzw. einer collection, falls nicht die default collection benutzt werden soll
+
+
+ #Verbinde mit der solt Instance
+
+ def connect(self):
+ self._v_solr=sunburnt.SolrInterface(url=self.solrURL)
+
+ #erzeuge einen Link, insbesonder für faceted suche
+ #@param search: ist suchparameter, wird einfach als search=%s weitergereicht
+ #@param facetSearch: bekommt einen hash (feldname, liste der suchworte)
+
+ def generateLink(self,search,facetSearch={},ranges={}):
+ ret="?search=%s"%search
+
+ for facet in facetSearch.keys():
+
+ searchTerms = facetSearch[facet]
+
+ if isinstance(searchTerms, basestring):
+ searchTerms=[searchTerms]
+
+ for searchTerm in searchTerms:
+ try:
+ ret+="&%s_fc=%s"%(facet,searchTerm.encode('utf-8'))
+ except:
+ ret+="&%s_fc=%s"%(facet,searchTerm)
+
+ for key,values in ranges.items():
+
+ if isinstance(values, basestring):
+ values=[values]
+
+ for value in values:
+ ret+="&%s_rg=%s"%(key,value)
+
+
+ return ret
+
+ #hilfsmethode erzeuget immer eine liste von einträgen
+ def getList(self,param):
+ if isinstance(param, basestring):
+ param=[param]
+
+ return param
+
+ #erzeugt analog zu den Ranges in Velocity für ein numerisches Feld die Suche in ranges
+ #@param field:ist der name des Feldes in dem in Ranges gesucht werden soll
+ #@param begin anfang der ranges
+ #@param end ende der Raanges
+ #@param increment größe eines ranges
+ # für alle andere paramter @see prepareSearch
+ #@return gibt ein hash: mit range -> anzahl der Treffer in dem Ranage. e.g 1921-1950 -> 21
+ def ranges(self,field,begin,end,increment,solrQuery="",facetFields=[],start=0,rows=10,facetSearch=None,sortFacets=True):
+
+ q=self.prepareSearch(solrQuery,facetFields,start,rows,facetSearch,sortFacets)
+
+ res={}
+ if not getattr(self,'_v_solr_',None):
+ self.connect()
+
+
+
+ for x in range(begin,end,increment):
+ query={}
+ #query["%s__gt"%field]=x
+ #TODO __gt scheint nicht zu funktionieren wird zu gte (???)
+ query["%s__gte"%field]=int(x)+1
+
+ query["%s__lte"%field]=x+increment
+
+ result = q.query(**query).execute()
+
+ res["%s-%s"%(x,x+increment)]=result.result.numFound
+
+ return res
+
+
+ #prepareSearch erzeugt die Suchabfrage
+
+ #solrQuery sucht im in schema.xml bzw. solrconfig.xml festgelegt generischen Feld, hierbei werden mit blanks getrennte eintrage in "AND" zerlegt.
+ #TODO: erlaube auch suche nach phrasen mit ""
+ #facetFields:Liste der Felder, nach denen facitiert werden sollen, wirdn in facet_by in solrburn uebersetzt.
+ #start: Erste Eintrag für paginierung, anzahl der Treffer
+ #rows: anzahl der Treffer
+ #facetSearch: Hash mit Feldnamen: suchwort oder Feldname: liste von Suchworten, hast wird direkt an query von solrburnt weitergegeben,
+ #sortfacets: if true, dann werden die Ergebnisse der facetierten suche alphabetisch sortiert, ACHTUNG: das ist nicht gleich der Funktion in solr die Liste
+ #direkt sortiert zurückzubekommen, hier werden die haufigsten Werte genommen (einstellt in solrconfig.xml) und dann nur diese sortiert!
+ #ausserdem werden beim sortieren, die stopworte gefiltert!
+ #
+ #neben den direkten parameter koennen auch parameter fuer die facetierte Suche über FORM im REQUEST übergeben werden, diese Felder müssen dann
+ #mit "_fc" enden.
+ # gibt als ergebnis den folgenden Hash, so wie in http://opensource.timetric.com/sunburnt/queryingsolr.html#executing-queries-and-interpreting-the-response,
+ # http://opensource.timetric.com/sunburnt/queryingsolr.html#highlighting
+ # und http://opensource.timetric.com/sunburnt/queryingsolr.html#faceting
+ #dokumentiert. Highlighting selbst wird in sorlconfig.xml konfiguriert.
+ #return ein Queryobjet, zur eigentlichen Suche muss darauf noch execute ausgeführt werden.
+
+ def prepareSearch(self,solrQuery,facetFields=[],start=0,rows=10,facetSearch=None,sortFacets=True):
+ "search solr"
+
+
+ ranges={}
+ ## deal with a form
+ if self.REQUEST:
+ constr = self.REQUEST.form
+ print constr
+ for field in constr.keys():
+
+ #facetes
+ if field.endswith("_fc"):
+ if facetSearch is None:
+ facetSearch={}
+
+ vals = constr[field]
+ if not isinstance(vals,basestring):
+
+ vals=[x.decode('utf-8') for x in vals]
+ else:
+ vals=vals.decode('utf-8')
+ facetSearch[field.replace("_fc",'')]=vals
+ #ranges form a-b
+
+
+ if field.endswith("_rg"):
+
+
+ splitted = "_".split(field)
+ if len(splitted)==2:
+ #ranges[field.replace("_rg","__gt")]=splitted[0]
+ #TODO __gt scheint nicht zu funktionieren wird zu gte (???)
+ ranges[field.replace("_rg","__gte")]=int(splitted[0])+1
+ ranges[field.replace("_rg","__lte")]=splitted[1]
+
+
+
+ #teste verbindung zu solr
+ if not getattr(self,'_v_solr_',None):
+ self.connect()
+
+
+
+ solrQuery = solrQuery.decode('utf-8')
+
+ #teile die suche nach " " daraus wird dann eine AND suche
+ #TODO: sollte flexibler sein. insbesondere phrasen
+ splitted= solrQuery.split(" ")
+
+ res = self._v_solr
+
+
+
+
+ res=res.query(splitted)
+ if len(ranges.keys())>0:
+ res=res.query(ranges)
+
+
+ #ubergebe alle weiteren feld an die suche.
+ if facetSearch:
+ for key,vals in facetSearch.items():
+
+ if key.endswith("_rg"):
+
+ if not isinstance(vals,basestring):
+
+ vals=[x.decode('utf-8') for x in vals]
+ else:
+ vals=[vals.decode('utf-8')]
+
+ for val in vals:
+
+ splitted = val.split("-")
+ print splitted
+ if len(splitted)==2:
+ #TODO __gt scheint nicht zu funktionieren wird zu gte (???)
+ facetSearch[key.replace("_rg","__gte")]=int(splitted[0])+1
+ facetSearch[key.replace("_rg","__lte")]=splitted[1]
+ print "I AM HERE 3"
+ print facetSearch
+ del facetSearch[key] # loesche das urspuerngliche feld
+
+ else:
+ val = [x for x in vals if x!="*"] #siehe oben
+ facetSearch[key]=val
+
+
+ res=res.query(**facetSearch)
+
+
+ #wenn facetField existieren dann rufe facetierung auf
+ if len(facetFields)>0:
+ #for facet in facetFields:
+
+ # res = res.facet_by(facet)
+ res = res.facet_by(facetFields)
+
+ #res=res.paginate(start=start, rows=rows).highlight("main_content")
+
+ res=res.paginate(start=start, rows=rows)
+
+
+
+ res=res.paginate(start=start, rows=rows).highlight()
+
+ return res
+
+
+ #für die parameter @see prepareSearch
+ #erzeugt eine Suchabfrage und führt diese aus.
+ #return {"result":response.result, "hl":response.highlighting,"facetFields":facetedFields}
+
+ def search(self,solrQuery,facetFields=[],start=0,rows=10,facetSearch=None,sortFacets=True):
+
+ res=self.prepareSearch(solrQuery,facetFields,start,rows,facetSearch,sortFacets)
+ response= res.execute()
+
+ #speichere faceted fields
+ if len(facetFields)>0:
+ facetedFields=response.facet_counts.facet_fields
+
+ if sortFacets:
+ facetedFields=self.sortFacetedFields(facetedFields)
+
+ else:
+ facetedFields={}
+
+
+
+
+ return {"result":response.result, "hl":response.highlighting,"facetFields":facetedFields}
+
+
+
+ #hilfsmethode zum sortieren über der ranges
+ def sortRanges(self,ranges):
+ x=list(ranges)
+ x.sort()
+ return x
+
+ #sortiert die Werte der FacetedFields
+ #(facetedFields ist ein hast mit feldname -> liste der (wert für das feld, anzahl der treffer für den wert)
+ #ausserdem werden die werte gemäß des angegebenen Filter gefiltert.
+
+ def sortFacetedFields(self, facetedFields,filter=STOPLIST):
+ ret={}
+
+ def cmpTuple(x,y):
+ return cmp(x[0],y[0])
+
+
+ for key in facetedFields.keys():
+ ls = facetedFields[key]
+ ls.sort(cmpTuple)
+ ret[key]=ls
+
+ if filter.get(key,None):
+ ls2=[]
+ for x in ls:
+ if x[0].encode('utf-8') not in filter[key]:
+ ls2.append(x)
+ ret[key]=ls2
+
+
+ ret[key]=[x for x in ret[key] if x[1]!=0]
+ return ret
+
+ def changeMain(self,solrURL=None,title=None,REQUEST=None,RESPONSE=None):
+ """change main settings"""
+ if solrURL:
+ self.solrURL=solrURL
+ self.title=title
+ self._v_solr=sunburnt.SolrInterface(url=solrURL)
+
+ if RESPONSE is not None:
+ RESPONSE.redirect('manage_main')
+
+
+ else:
+ pt=zptFile(self, 'zpt/ChangeZopeSolr.zpt')
+ return pt()
+
+ #sucht die je nach einsteillung in solrconfig.xml Werte mit den häufigsten Treffern oder alphabetisch sortiert zu einem
+ #bestimmten eintrag aus solr
+ #gedacht ist die methoden für die Anwendung nach dem Harvesten einer Website, es wird daher davon ausgegangen, dass sie hinter der
+ #idfield eine url steht. Wir es z.b. bei Nutch passiert. index.htm/index_html als Teil der url wird dabei unterdrückt (analog zu den harbest einstellunge für
+ # nutch für zope webseiten.
+ #@param @idfield is hierbei der Name des Feldes, das in solrschema als id defniert wurde
+ #field der Feldname von dem die Treffer gesurcht wernde sollen
+ #url die url des textes
+
+ def getTermsAsJSON(self,idfield,field,url):
+ """getTerms"""
+
+ ret=[]
+ h = httplib2.Http()
+ url = url.replace("/index.html","").replace("/index_html","")
+ if url[-1]=="/":
+ url=url[0:-1]
+
+ urlq=url.replace(":","\:")
+ urlq=urlq.replace("/","\/")
+
+ q ="http://localhost:8983/solr/mpiwgweb/select?q=%s:%s&wt=xml&tv=on&qt=tvrh&fl=%s&tv.tf=true"%(idfield,urlq,field)
+
+ resp, content = h.request(q)
+
+ root = ET.fromstring(content)
+ #uri = "http://127.0.0.1:18080/www_neu/de/aktuelles/features/feature28"
+
+ xpstr = ".//lst[@name='termVectors']/lst[@name='%s']/lst[@name='%s']/lst"%(url,field)
+
+
+ tvs = root.findall(xpstr)
+
+ for tv in tvs:
+ wd = tv.attrib['name']
+ for f in tv.findall("./int[@name='tf']"):
+ fre = f.text
+
+
+ if int(fre)>2:
+
+ ret.append('{"text":"%s","size":%s}'%(wd,fre))
+
+ retStr="["+",".join(ret)+"]"
+
+ return retStr
+
+ #tauscht im request die in neewparams angegeben parameter aus.
+ def replaceParam(self, newparams):
+ x = self.REQUEST.form.copy()
+
+ for key,value in newparams.items():
+ x[key]=value
+
+
+ retls=[]
+ for k,v in x.items():
+ if not isinstance(v,basestring):
+ for y in v:
+ retls.append((k,y))
+ else:
+ retls.append((k,v))
+
+ return "?"+"&".join(["%s=%s"%(k,urllib.quote_plus(v,'/')) for (k, v) in retls])
+
+
+
+
+ #ruft @set ranges aus, gibt das ergebnis als json zurück
+ def getRangesAsJSON(self,field,begin,end,increment):
+ """ getRangesAsJSON"""
+ res = self.ranges(field, int(begin), int(end), int(increment))
+ return json.dumps(res)
+
+
+ #return only the values of resultList whicht start with startLetter or if starLetterNonAscii all values which
+ #start with an non ascii character
+ def filter (self,resultList,startLetter=None,startLetterNonAscii=0):
+ if startLetter:
+
+ matchStr = "[\[\]'\"]*"+startLetter
+ ls = [x for x in resultList if re.match(matchStr,x[0])]
+
+ if startLetterNonAscii ==1:
+
+ ls = [x for x in resultList if not re.match("[\[\]'\"a-zA-Z].*",x[0])]
+ return ls
+
+def manage_addZopeSolrForm(self):
+ """Form for external Links"""
+ pt=zptFile(self, 'zpt/AddZopeSolr.zpt')
+ return pt()
+
+
+def manage_addZopeSolr(self,id,title,solrURL,RESPONSE=None):
+ """Add an external Link"""
+
+ newObj=ZopeSolr(id,title,solrURL)
+
+ self._setObject(id,newObj)
+
+
+ if RESPONSE is not None:
+ RESPONSE.redirect('manage_main')
+
\ No newline at end of file
diff -r 000000000000 -r 834706423ac1 zpt/AddZopeSolr.zpt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/zpt/AddZopeSolr.zpt Tue Feb 26 15:22:07 2013 +0100
@@ -0,0 +1,14 @@
+Header
+ Add a new Ressource
+
+Footer
+
\ No newline at end of file
diff -r 000000000000 -r 834706423ac1 zpt/ChangeZopeSolr.zpt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/zpt/ChangeZopeSolr.zpt Tue Feb 26 15:22:07 2013 +0100
@@ -0,0 +1,14 @@
+Header
+ Add a new Ressource
+
+
+Footer
+
\ No newline at end of file